forked from IQ.Lvbs/IQ.Pilot
IQ.Pilot Release Commit @ 24db8ae
This commit is contained in:
@@ -15,6 +15,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"AthenadPid", {PERSISTENT, INT}},
|
||||
{"AthenadUploadQueue", {PERSISTENT, JSON}},
|
||||
{"AthenadRecentlyViewedRoutes", {PERSISTENT, STRING}},
|
||||
{"IQUploaderDeferred", {PERSISTENT, JSON}},
|
||||
{"BackupManagerK3_CreateBackup", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"BackupManagerK3_RestoreVersion", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"BootCount", {PERSISTENT, INT}},
|
||||
@@ -269,6 +270,32 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"iqMqbAccResume", {PERSISTENT, BOOL, "0"}},
|
||||
{"iqMqbSteeringLockout", {PERSISTENT, BOOL, "0"}},
|
||||
{"AllowLateralWhenLongUnavailable", {PERSISTENT, BOOL}},
|
||||
{"IQEmacEnabled", {PERSISTENT, BOOL, "0"}},
|
||||
{"IQEmacHost", {PERSISTENT, STRING}},
|
||||
{"IQEmacModel", {PERSISTENT, STRING}},
|
||||
{"IQEmacCatalogCache", {PERSISTENT, JSON}},
|
||||
{"IQEgpuDisabled", {PERSISTENT, BOOL, "0"}},
|
||||
{"IQEgpuEnabled", {PERSISTENT, BOOL, "0"}},
|
||||
{"MacModelStatus", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"MacModelLatencyMs", {CLEAR_ON_MANAGER_START, FLOAT}},
|
||||
{"MacModelMissRate", {CLEAR_ON_MANAGER_START, FLOAT}},
|
||||
{"MacModelPresent", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"MacModelReachable", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"MacModelCompiled", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"MacModelReady", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"MacModelActive", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"MacModelFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"MacModelLastError", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"MacModelDownloadProgress", {CLEAR_ON_MANAGER_START, FLOAT}},
|
||||
{"UsbGpuStatus", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"UsbGpuLatencyMs", {CLEAR_ON_MANAGER_START, FLOAT}},
|
||||
{"UsbGpuActive", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"UsbGpuFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"UsbGpuPresent", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"UsbGpuLastError", {CLEAR_ON_MANAGER_START, STRING}},
|
||||
{"UsbGpuSetupProgress", {CLEAR_ON_MANAGER_START, FLOAT}},
|
||||
{"UsbGpuCompiled", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"UsbGpuLoading", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
|
||||
{"IQDynamicMode", {PERSISTENT, BOOL, "0"}},
|
||||
{"IQDynamicBlendStockRadar", {PERSISTENT, BOOL, "0"}},
|
||||
|
||||
@@ -21,11 +21,21 @@ except Exception: # ProprietaryModuleMissing or import errors in stripped build
|
||||
_private_base_urls = None
|
||||
_private_auth = None
|
||||
|
||||
DEFAULT_TILE_BUNDLE_BASE_URL = "https://maps.konn3kt.com/iqosmd/v1"
|
||||
# Tile bundles live as LFS objects in the PRIVATE repo IQ.Lvbs/iqmaps (R2 is gone).
|
||||
# Anonymous access 404s by design; devices authenticate with the embedded read-only PAT
|
||||
# carried by the closed-source updater bundle (same fetch account as the OS images).
|
||||
DEFAULT_TILE_BUNDLE_BASE_URL = "https://git.konn3kt.com/IQ.Lvbs/iqmaps/raw/branch/master"
|
||||
FALLBACK_TILE_BUNDLE_BASE_URL = "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqmaps/raw/branch/master"
|
||||
|
||||
# Gitea /raw NEVER returns LFS content -- it returns this pointer, and the real bytes come
|
||||
# from the LFS batch API (see _resolve_object_url).
|
||||
LFS_POINTER_MAGIC = b"version https://git-lfs"
|
||||
BASE_URL_PARAM = "OfflineTilesBaseUrl"
|
||||
PROGRESS_PARAM = "OfflineTilesDownloadProgress"
|
||||
REQUEST_PARAM = "OfflineTilesDownloadRequest"
|
||||
CHUNK_BYTES = 1 << 20
|
||||
# must match scripts/iqpilot/tile_factory/upload_bundles_lfs.py
|
||||
PART_BYTES = 90 * 1024 * 1024
|
||||
HTTP_TIMEOUT_S = 30.0
|
||||
STREAM_RETRIES = 8
|
||||
|
||||
@@ -44,9 +54,52 @@ def candidate_base_urls(params: Params) -> list[str]:
|
||||
except Exception:
|
||||
pass
|
||||
urls.append(DEFAULT_TILE_BUNDLE_BASE_URL)
|
||||
urls.append(FALLBACK_TILE_BUNDLE_BASE_URL)
|
||||
return urls
|
||||
|
||||
|
||||
def _maps_auth_module():
|
||||
"""The read PAT lives in the compiled updater bundle (never in this open file)."""
|
||||
try:
|
||||
from iqpilot.system.proprietary_runtime._verified_import import import_verified_module
|
||||
return import_verified_module("iqpilot_updater_private", "iqpilot_private.updater.git_remote")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
bundle_python = os.path.join(root, "artifacts", "iqpilot_updater_private", "python")
|
||||
if os.path.isdir(bundle_python):
|
||||
if bundle_python not in sys.path:
|
||||
sys.path.insert(0, bundle_python)
|
||||
return importlib.import_module("iqpilot_private.updater.git_remote")
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def request_headers(url: str) -> dict:
|
||||
mod = _maps_auth_module()
|
||||
if mod is not None:
|
||||
try:
|
||||
headers = mod.map_tiles_headers(url)
|
||||
if headers:
|
||||
return headers
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
from iqpilot.common.git_creds import get_credentials
|
||||
creds = get_credentials()
|
||||
if creds and all(creds) and "/iq.lvbs/iqmaps" in url.lower():
|
||||
import base64
|
||||
return {"Authorization": "Basic " + base64.b64encode(f"{creds[0]}:{creds[1]}".encode()).decode()}
|
||||
except Exception:
|
||||
pass
|
||||
return {}
|
||||
|
||||
|
||||
def request_auth() -> tuple[str, str] | None:
|
||||
if _private_auth is None:
|
||||
return None
|
||||
@@ -56,8 +109,67 @@ def request_auth() -> tuple[str, str] | None:
|
||||
return None
|
||||
|
||||
|
||||
def _lfs_endpoint(base_url: str) -> str:
|
||||
"""<host>/<owner>/<repo>/raw/branch/<b> -> <host>/<owner>/<repo>.git/info/lfs"""
|
||||
return base_url.split("/raw/", 1)[0] + ".git/info/lfs"
|
||||
|
||||
|
||||
def _resolve_oid_url(session: requests.Session, base_url: str, oid: str, size: int,
|
||||
headers: dict) -> tuple[str, dict]:
|
||||
"""Bundles are stored as bare LFS objects addressed by oid from the index -- no pointer
|
||||
files, because committing one per part meant hundreds of concurrent commits per branch."""
|
||||
batch = session.post(f"{_lfs_endpoint(base_url)}/objects/batch",
|
||||
data=json.dumps({"operation": "download", "transfers": ["basic"],
|
||||
"objects": [{"oid": oid, "size": size}]}),
|
||||
headers={"Content-Type": "application/vnd.git-lfs+json",
|
||||
"Accept": "application/vnd.git-lfs+json", **headers},
|
||||
timeout=HTTP_TIMEOUT_S)
|
||||
batch.raise_for_status()
|
||||
entry = batch.json()["objects"][0]
|
||||
if "actions" not in entry:
|
||||
raise requests.RequestException(f"LFS object unavailable: {entry.get('error', oid)}")
|
||||
action = entry["actions"]["download"]
|
||||
return action["href"], action.get("header", {})
|
||||
|
||||
|
||||
def _resolve_object_url(session: requests.Session, url: str, headers: dict) -> tuple[str, dict]:
|
||||
"""Follow a Gitea LFS pointer to the real (pre-signed) object URL.
|
||||
|
||||
Returns the URL to stream plus any extra headers it needs. A plain host that serves the
|
||||
bytes directly (local test server, static mirror) resolves to itself unchanged."""
|
||||
probe = session.get(url, headers={**headers, "Accept-Encoding": None}, stream=True,
|
||||
timeout=HTTP_TIMEOUT_S)
|
||||
probe.raise_for_status()
|
||||
if int(probe.headers.get("content-length") or 0) >= 1024:
|
||||
probe.close()
|
||||
return url, {}
|
||||
body = probe.content
|
||||
probe.close()
|
||||
if not body.startswith(LFS_POINTER_MAGIC):
|
||||
return url, {}
|
||||
|
||||
meta = dict(line.split(" ", 1) for line in body.decode().strip().splitlines() if " " in line)
|
||||
oid = meta["oid"].split(":", 1)[1]
|
||||
size = int(meta["size"])
|
||||
lfs_base = url.split("/raw/", 1)[0] + ".git/info/lfs"
|
||||
batch = session.post(f"{lfs_base}/objects/batch",
|
||||
data=json.dumps({"operation": "download", "transfers": ["basic"],
|
||||
"objects": [{"oid": oid, "size": size}]}),
|
||||
headers={"Content-Type": "application/vnd.git-lfs+json",
|
||||
"Accept": "application/vnd.git-lfs+json", **headers},
|
||||
timeout=HTTP_TIMEOUT_S)
|
||||
batch.raise_for_status()
|
||||
action = batch.json()["objects"][0]["actions"]["download"]
|
||||
return action["href"], action.get("header", {})
|
||||
|
||||
|
||||
def fetch_index(base_url: str, session: requests.Session) -> dict:
|
||||
response = session.get(f"{base_url}/index.json", timeout=HTTP_TIMEOUT_S, auth=request_auth())
|
||||
index_url = f"{base_url}/index.json"
|
||||
headers = request_headers(index_url)
|
||||
# requests' auth= rewrites the Authorization header, so only fall back to it when the
|
||||
# closed-source bundle gave us nothing.
|
||||
response = session.get(index_url, timeout=HTTP_TIMEOUT_S, headers=headers,
|
||||
auth=None if headers else request_auth())
|
||||
response.raise_for_status()
|
||||
index = response.json()
|
||||
regions = index.get("regions")
|
||||
@@ -164,7 +276,7 @@ class TileBundleDownloader:
|
||||
ok = self._download_file(
|
||||
selector, base_url, entry["path"], int(entry.get("bytes", 0)),
|
||||
str(entry.get("sha256", "")).strip().lower(), night_path,
|
||||
progress_offset, progress_total,
|
||||
progress_offset, progress_total, int(entry.get("parts", 1)), entry.get("objects"),
|
||||
)
|
||||
if not ok:
|
||||
return False
|
||||
@@ -174,6 +286,7 @@ class TileBundleDownloader:
|
||||
str(entry.get("day_sha256", "")).strip().lower(),
|
||||
night_path.with_name("offline_day.mbtiles"),
|
||||
progress_offset + int(entry.get("bytes", 0)), progress_total,
|
||||
int(entry.get("day_parts", 1)), entry.get("day_objects"),
|
||||
)
|
||||
if not day_ok:
|
||||
cloudlog.warning(f"iq_maps: day-style bundle failed for {selector}; night set installed")
|
||||
@@ -183,8 +296,16 @@ class TileBundleDownloader:
|
||||
|
||||
def _download_file(self, selector: str, base_url: str, remote_path: str, expected_bytes: int,
|
||||
expected_sha: str, final_path: Path,
|
||||
progress_offset: int, progress_total: int) -> bool:
|
||||
url = f"{base_url}/{remote_path.lstrip('/')}"
|
||||
progress_offset: int, progress_total: int, parts: int = 1,
|
||||
objects: list | None = None) -> bool:
|
||||
# Bundles are published as <name>.pNN because Cloudflare caps proxied bodies at ~100MB.
|
||||
# They stream back-to-back into ONE .part file: concatenating afterwards would need
|
||||
# double the free space, which devices do not have.
|
||||
base = f"{base_url}/{remote_path.lstrip('/')}"
|
||||
if objects:
|
||||
urls = [None] * len(objects) # resolved per-attempt from the oid
|
||||
else:
|
||||
urls = [base] if parts <= 1 else [f"{base}.p{i:02d}" for i in range(parts)]
|
||||
part_path = final_path.with_name(final_path.name + ".part")
|
||||
part_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -207,28 +328,45 @@ class TileBundleDownloader:
|
||||
digest = hashlib.sha256()
|
||||
resume_from = 0
|
||||
|
||||
headers = {"Range": f"bytes={resume_from}-"} if resume_from else {}
|
||||
auth = request_auth()
|
||||
response = self.session.get(url, headers=headers, stream=True, timeout=HTTP_TIMEOUT_S, auth=auth)
|
||||
if resume_from and response.status_code != 206:
|
||||
digest = hashlib.sha256()
|
||||
resume_from = 0
|
||||
part_path.unlink(missing_ok=True)
|
||||
if response.status_code == 416:
|
||||
response = self.session.get(url, stream=True, timeout=HTTP_TIMEOUT_S, auth=auth)
|
||||
response.raise_for_status()
|
||||
|
||||
# every part but the last is exactly PART_BYTES, so a byte offset maps to a part index
|
||||
first_part = resume_from // PART_BYTES if len(urls) > 1 else 0
|
||||
skip_in_part = resume_from - first_part * PART_BYTES if len(urls) > 1 else resume_from
|
||||
downloaded = resume_from
|
||||
mode = "ab" if resume_from else "wb"
|
||||
with open(part_path, mode) as f:
|
||||
for chunk in response.iter_content(chunk_size=CHUNK_BYTES):
|
||||
if self._should_abort():
|
||||
cloudlog.warning(f"iq_maps: tile bundle download cancelled for {selector}")
|
||||
return False
|
||||
f.write(chunk)
|
||||
digest.update(chunk)
|
||||
downloaded += len(chunk)
|
||||
self._publish_progress(selector, progress_offset + downloaded, progress_total, active=True)
|
||||
for index in range(first_part, len(urls)):
|
||||
if objects:
|
||||
url_headers = request_headers(base_url)
|
||||
auth = None if url_headers else request_auth()
|
||||
object_url, object_headers = _resolve_oid_url(
|
||||
self.session, base_url, objects[index]["oid"], int(objects[index]["size"]),
|
||||
url_headers)
|
||||
else:
|
||||
url = urls[index]
|
||||
url_headers = request_headers(url)
|
||||
auth = None if url_headers else request_auth()
|
||||
# Re-resolve per part: a pre-signed LFS object URL can expire mid-download.
|
||||
object_url, object_headers = _resolve_object_url(self.session, url, url_headers)
|
||||
headers = dict(object_headers)
|
||||
offset = skip_in_part if index == first_part else 0
|
||||
if offset:
|
||||
headers["Range"] = f"bytes={offset}-"
|
||||
response = self.session.get(object_url, headers=headers, stream=True,
|
||||
timeout=HTTP_TIMEOUT_S, auth=auth)
|
||||
if offset and response.status_code != 206:
|
||||
# server ignored the range: restart this whole file cleanly
|
||||
f.close()
|
||||
part_path.unlink(missing_ok=True)
|
||||
raise requests.RequestException(f"range not honoured for part {index}")
|
||||
response.raise_for_status()
|
||||
for chunk in response.iter_content(chunk_size=CHUNK_BYTES):
|
||||
if self._should_abort():
|
||||
cloudlog.warning(f"iq_maps: tile bundle download cancelled for {selector}")
|
||||
return False
|
||||
f.write(chunk)
|
||||
digest.update(chunk)
|
||||
downloaded += len(chunk)
|
||||
self._publish_progress(selector, progress_offset + downloaded, progress_total, active=True)
|
||||
break
|
||||
except requests.RequestException as exc:
|
||||
last_error = exc
|
||||
|
||||
@@ -48,6 +48,7 @@ CTRL_FLAG_MISSING_INPUTS = 1 << 0
|
||||
CTRL_FLAG_RK_OVERRUN = 1 << 1
|
||||
SMOOTH_STEER_W0 = 46.0
|
||||
SMOOTH_STEER_ZETA = 1.0
|
||||
SMOOTH_STEER_HOLD_FRAMES = 50
|
||||
LAT_SMOOTH_SECONDS = 0.0
|
||||
|
||||
|
||||
@@ -100,6 +101,7 @@ class Controls(IQControlsLayer):
|
||||
self.VM = VehicleModel(self.CP)
|
||||
|
||||
self.smooth_steer = PT2Filter(SMOOTH_STEER_W0, SMOOTH_STEER_ZETA, DT_CTRL)
|
||||
self.smooth_steer_inactive_frames = SMOOTH_STEER_HOLD_FRAMES
|
||||
self.is_curvature_car = self.CP.steerControlType == car.CarParams.SteerControlType.curvatureDEPRECATED
|
||||
if self.is_curvature_car and self.params.get("EnableSmoothSteer") is None:
|
||||
self.params.put_bool("EnableSmoothSteer", True)
|
||||
@@ -235,7 +237,8 @@ class Controls(IQControlsLayer):
|
||||
else:
|
||||
new_desired_curvature = model_v2.action.desiredCurvature
|
||||
|
||||
if self.is_curvature_car and self.enable_smooth_steer and CC.latActive:
|
||||
self.smooth_steer_inactive_frames = 0 if CC.latActive else self.smooth_steer_inactive_frames + 1
|
||||
if self.is_curvature_car and self.enable_smooth_steer and self.smooth_steer_inactive_frames < SMOOTH_STEER_HOLD_FRAMES:
|
||||
new_desired_curvature = self.smooth_steer.update(new_desired_curvature)
|
||||
else:
|
||||
self.smooth_steer.reset(new_desired_curvature)
|
||||
|
||||
@@ -13,7 +13,8 @@ def dmonitoringd_thread():
|
||||
sm = messaging.SubMaster(['driverStateV2', 'extrinsicsCalibration', 'carState', 'selfdriveState', 'modelV2',
|
||||
'carControl'], poll='driverStateV2')
|
||||
|
||||
DM = DriverMonitoring(rhd_saved=params.get_bool("IsRhdDetected"), always_on=params.get_bool("AlwaysOnDM"))
|
||||
DM = DriverMonitoring(rhd_saved=params.get_bool("IsRhdDetected"), always_on=params.get_bool("AlwaysOnDM"),
|
||||
force_rhd=params.get_bool("ForceRHDForBSM"))
|
||||
demo_mode=False
|
||||
|
||||
# 20Hz <- dmonitoringmodeld
|
||||
@@ -36,10 +37,11 @@ def dmonitoringd_thread():
|
||||
# load live always-on toggle
|
||||
if sm['driverStateV2'].frameId % 40 == 1:
|
||||
DM.always_on = params.get_bool("AlwaysOnDM")
|
||||
DM.force_rhd = params.get_bool("ForceRHDForBSM")
|
||||
demo_mode = params.get_bool("IsDriverViewEnabled")
|
||||
|
||||
# save rhd virtual toggle every 5 mins
|
||||
if (sm['driverStateV2'].frameId % 6000 == 0 and not demo_mode and
|
||||
if (sm['driverStateV2'].frameId % 6000 == 0 and not demo_mode and not DM.force_rhd and
|
||||
DM.wheelpos.prob_offseter.filtered_stat.n > DM.settings._WHEELPOS_FILTER_MIN_COUNT and
|
||||
DM.wheel_on_right == (DM.wheelpos.prob_offseter.filtered_stat.M > DM.settings._WHEELPOS_THRESHOLD)):
|
||||
params.put_bool_nonblocking("IsRhdDetected", DM.wheel_on_right)
|
||||
|
||||
@@ -145,7 +145,7 @@ def face_orientation_from_net(angles_desc, pos_desc, rpy_calib):
|
||||
|
||||
|
||||
class DriverMonitoring:
|
||||
def __init__(self, rhd_saved=False, settings=None, always_on=False):
|
||||
def __init__(self, rhd_saved=False, settings=None, always_on=False, force_rhd=False):
|
||||
# init policy settings
|
||||
self.settings = settings if settings is not None else DRIVER_MONITOR_SETTINGS(device_type=HARDWARE.get_device_type())
|
||||
|
||||
@@ -158,6 +158,7 @@ class DriverMonitoring:
|
||||
self.blink = DriverBlink()
|
||||
|
||||
self.always_on = always_on
|
||||
self.force_rhd = force_rhd
|
||||
self.distracted_types = []
|
||||
self.driver_distracted = False
|
||||
self.driver_distraction_filter = FirstOrderFilter(0., self.settings._DISTRACTED_FILTER_TS, self.settings._DT_DMON)
|
||||
@@ -268,18 +269,20 @@ class DriverMonitoring:
|
||||
def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstill, demo_mode=False):
|
||||
rhd_pred = driver_state.wheelOnRightProb
|
||||
# calibrates only when there's movement and either face detected
|
||||
if car_speed > self.settings._WHEELPOS_CALIB_MIN_SPEED and (driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLD or
|
||||
if not self.force_rhd and car_speed > self.settings._WHEELPOS_CALIB_MIN_SPEED and (driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLD or
|
||||
driver_state.rightDriverData.faceProb > self.settings._FACE_THRESHOLD):
|
||||
self.wheelpos.prob_offseter.push_and_update(rhd_pred)
|
||||
|
||||
self.wheelpos.prob_calibrated = self.wheelpos.prob_offseter.filtered_stat.n > self.settings._WHEELPOS_FILTER_MIN_COUNT
|
||||
|
||||
if self.wheelpos.prob_calibrated or demo_mode:
|
||||
if self.force_rhd:
|
||||
self.wheel_on_right = True
|
||||
elif self.wheelpos.prob_calibrated or demo_mode:
|
||||
self.wheel_on_right = self.wheelpos.prob_offseter.filtered_stat.M > self.settings._WHEELPOS_THRESHOLD
|
||||
else:
|
||||
self.wheel_on_right = self.wheel_on_right_default # use default/saved if calibration is unfinished
|
||||
# make sure no switching when engaged
|
||||
if op_engaged and self.wheel_on_right_last is not None and self.wheel_on_right_last != self.wheel_on_right and not demo_mode:
|
||||
if not self.force_rhd and op_engaged and self.wheel_on_right_last is not None and self.wheel_on_right_last != self.wheel_on_right and not demo_mode:
|
||||
self.wheel_on_right = self.wheel_on_right_last
|
||||
driver_data = driver_state.rightDriverData if self.wheel_on_right else driver_state.leftDriverData
|
||||
if not all(len(x) > 0 for x in (driver_data.faceOrientation, driver_data.facePosition,
|
||||
|
||||
@@ -247,6 +247,22 @@ class TestMonitoring:
|
||||
dm._update_states(ds, [0., 0., 0.], 20.0, True, False)
|
||||
assert dm.wheel_on_right
|
||||
|
||||
def test_state_update_forces_rhd(self):
|
||||
dm = DriverMonitoring(force_rhd=True)
|
||||
dm.wheel_on_right_last = False
|
||||
dm.wheelpos.prob_offseter.filtered_stat.M = 0.0
|
||||
dm.wheelpos.prob_offseter.filtered_stat.n = dm.settings._WHEELPOS_FILTER_MIN_COUNT + 1
|
||||
ds = log.DriverStateV2.new_message()
|
||||
ds.wheelOnRightProb = 0.0
|
||||
set_driver_data(ds.rightDriverData)
|
||||
|
||||
dm._update_states(ds, [0., 0., 0.], 20.0, True, False)
|
||||
|
||||
assert dm.wheel_on_right
|
||||
assert dm.face_detected
|
||||
assert dm.wheelpos.prob_offseter.filtered_stat.n == dm.settings._WHEELPOS_FILTER_MIN_COUNT + 1
|
||||
assert dm.get_state_packet().driverMonitoringState.isRHD
|
||||
|
||||
def test_state_update_rejects_incomplete_driver_data(self):
|
||||
dm = DriverMonitoring()
|
||||
dm._update_states(log.DriverStateV2.new_message(), [0., 0., 0.], 0.0, False, False)
|
||||
|
||||
@@ -93,6 +93,33 @@ class EngagedConfirmationButton(BigButton):
|
||||
self.set_click_callback(lambda: _engaged_confirmation_click(callback, action_text, icon, exit_on_confirm=exit_on_confirm, red=red))
|
||||
|
||||
|
||||
class ForceOffroadButton(BigButton):
|
||||
def __init__(self):
|
||||
self._offroad_icon = gui_app.texture("icons/iq/square-parking.png", 64, 64)
|
||||
super().__init__(tr("force\noffroad"), "", self._offroad_icon)
|
||||
self.set_press_effect_enabled(False)
|
||||
self._label.set_font_size(40)
|
||||
self._label.set_line_height(0.95)
|
||||
self.set_click_callback(self._on_click)
|
||||
self._sync_from_params()
|
||||
|
||||
def _forced(self) -> bool:
|
||||
return ui_state.params.get_bool("IQAlwaysOffroad")
|
||||
|
||||
def _on_click(self):
|
||||
forced = self._forced()
|
||||
action = tr("disable force offroad") if forced else tr("force offroad")
|
||||
_engaged_confirmation_click(lambda: ui_state.params.put_bool("IQAlwaysOffroad", not forced),
|
||||
action, self._offroad_icon, exit_on_confirm=False)
|
||||
|
||||
def _sync_from_params(self):
|
||||
self.set_text(tr("disable\nforce offroad") if self._forced() else tr("force\noffroad"))
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
self._sync_from_params()
|
||||
|
||||
|
||||
class DeviceInfoLayoutMici(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -226,6 +253,7 @@ class DeviceLayoutMici(NavScroller):
|
||||
terms_btn,
|
||||
regulatory_btn,
|
||||
reset_calibration_btn,
|
||||
ForceOffroadButton(),
|
||||
reboot_btn,
|
||||
self._power_off_btn,
|
||||
])
|
||||
|
||||
@@ -1830,6 +1830,31 @@ msgstr ""
|
||||
msgid "power off"
|
||||
msgstr ""
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr ""
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr ""
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/onboarding.py:100
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
|
||||
@@ -3386,6 +3386,20 @@ msgstr "الجهاز"
|
||||
msgid "device ID"
|
||||
msgstr "معرّف الجهاز"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"إلغاء\n"
|
||||
"فرض التوقف"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "إلغاء فرض وضع التوقف"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3543,11 +3557,26 @@ msgstr "لـ \"{}\""
|
||||
msgid "for private branch updates"
|
||||
msgstr "لتحديثات الفروع الخاصة"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"فرض\n"
|
||||
"وضع التوقف"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "فرض واجهة mici"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "فرض وضع التوقف"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3453,6 +3453,20 @@ msgstr "gerät"
|
||||
msgid "device ID"
|
||||
msgstr "geräte-ID"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"parkmodus\n"
|
||||
"freigeben"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "parkmodus freigeben"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3610,11 +3624,26 @@ msgstr "für „{}“"
|
||||
msgid "for private branch updates"
|
||||
msgstr "für Updates aus privaten Branches"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"parkmodus\n"
|
||||
"erzwingen"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "mici-oberfläche erzwingen"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "parkmodus erzwingen"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3411,6 +3411,20 @@ msgstr "device"
|
||||
msgid "device ID"
|
||||
msgstr "device ID"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "disable force offroad"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3568,11 +3582,26 @@ msgstr "for \"{}\""
|
||||
msgid "for private branch updates"
|
||||
msgstr "for private branch updates"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "force mici UI"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "force offroad"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3458,6 +3458,20 @@ msgstr "dispositivo"
|
||||
msgid "device ID"
|
||||
msgstr "ID del dispositivo"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"salir de\n"
|
||||
"aparcado"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "desactivar aparcado forzado"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3615,11 +3629,26 @@ msgstr "para «{}»"
|
||||
msgid "for private branch updates"
|
||||
msgstr "para actualizaciones de ramas privadas"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"forzar\n"
|
||||
"aparcado"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "forzar interfaz mici"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "forzar aparcado"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3475,6 +3475,20 @@ msgstr "appareil"
|
||||
msgid "device ID"
|
||||
msgstr "ID appareil"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"quitter\n"
|
||||
"hors route"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "désactiver hors route forcé"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3632,11 +3646,26 @@ msgstr "pour \"{}\""
|
||||
msgid "for private branch updates"
|
||||
msgstr "pour les mises à jour de branches privées"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"forcer\n"
|
||||
"hors route"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "forcer l'interface mici"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "forcer hors route"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3281,6 +3281,20 @@ msgstr "デバイス"
|
||||
msgid "device ID"
|
||||
msgstr "デバイスID"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"強制オフロード\n"
|
||||
"解除"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "強制オフロードを解除"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3438,11 +3452,26 @@ msgstr "「{}」向け"
|
||||
msgid "for private branch updates"
|
||||
msgstr "プライベートブランチの更新用"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"強制\n"
|
||||
"オフロード"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "mici UIを強制"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "強制オフロード"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3303,6 +3303,20 @@ msgstr "기기"
|
||||
msgid "device ID"
|
||||
msgstr "기기 ID"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"강제 오프로드\n"
|
||||
"해제"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "강제 오프로드 해제"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3460,11 +3474,26 @@ msgstr "\"{}\"용"
|
||||
msgid "for private branch updates"
|
||||
msgstr "비공개 브랜치 업데이트용"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"강제\n"
|
||||
"오프로드"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "mici UI 강제"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "강제 오프로드"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3450,6 +3450,20 @@ msgstr "urządzenie"
|
||||
msgid "device ID"
|
||||
msgstr "ID urządzenia"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"wyłącz\n"
|
||||
"wymuszenie"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "wyłącz wymuszony postój"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3607,11 +3621,26 @@ msgstr "dla „{}”"
|
||||
msgid "for private branch updates"
|
||||
msgstr "do aktualizacji z prywatnych gałęzi"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"wymuś\n"
|
||||
"postój"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "wymuś interfejs mici"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "wymuś postój"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3452,6 +3452,20 @@ msgstr "dispositivo"
|
||||
msgid "device ID"
|
||||
msgstr "ID do dispositivo"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"sair de\n"
|
||||
"estacionado"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "desativar estacionado forçado"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3609,11 +3623,26 @@ msgstr "para \"{}\""
|
||||
msgid "for private branch updates"
|
||||
msgstr "para atualizações de branches privadas"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"forçar\n"
|
||||
"estacionado"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "forçar interface mici"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "forçar estacionado"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3392,6 +3392,20 @@ msgstr "อุปกรณ์"
|
||||
msgid "device ID"
|
||||
msgstr "รหัสอุปกรณ์"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"ปิดโหมด\n"
|
||||
"จอดบังคับ"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "ปิดโหมดจอดแบบบังคับ"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3549,11 +3563,26 @@ msgstr "สำหรับ \"{}\""
|
||||
msgid "for private branch updates"
|
||||
msgstr "สำหรับการอัปเดตจากสาขาส่วนตัว"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"บังคับ\n"
|
||||
"โหมดจอด"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "บังคับใช้อินเทอร์เฟซ mici"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "บังคับโหมดจอด"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3427,6 +3427,20 @@ msgstr "cihaz"
|
||||
msgid "device ID"
|
||||
msgstr "cihaz kimliği"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"zorlamayı\n"
|
||||
"kapat"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "park zorlamasını kapat"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3584,11 +3598,26 @@ msgstr "\"{}\" için"
|
||||
msgid "for private branch updates"
|
||||
msgstr "özel dal güncellemeleri için"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"park modunu\n"
|
||||
"zorla"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "mici arayüzünü zorla"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "park modunu zorla"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3436,6 +3436,20 @@ msgstr "пристрій"
|
||||
msgid "device ID"
|
||||
msgstr "ID пристрою"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"вимкнути\n"
|
||||
"примус"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "вимкнути примусовий офроуд"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3593,11 +3607,26 @@ msgstr "для \"{}\""
|
||||
msgid "for private branch updates"
|
||||
msgstr "для оновлень із приватних гілок"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"офроуд\n"
|
||||
"примусово"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "примусовий інтерфейс mici"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "примусовий офроуд"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3260,6 +3260,20 @@ msgstr "设备"
|
||||
msgid "device ID"
|
||||
msgstr "设备 ID"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"关闭\n"
|
||||
"强制驻车"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "关闭强制驻车"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3417,11 +3431,26 @@ msgstr "用于“{}”"
|
||||
msgid "for private branch updates"
|
||||
msgstr "用于私有分支更新"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"强制\n"
|
||||
"驻车"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "强制使用 mici 界面"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "强制驻车"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -3262,6 +3262,20 @@ msgstr "裝置"
|
||||
msgid "device ID"
|
||||
msgstr "裝置 ID"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"disable\n"
|
||||
"force offroad"
|
||||
msgstr ""
|
||||
"關閉\n"
|
||||
"強制駐車"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "disable force offroad"
|
||||
msgstr "關閉強制駐車"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:56
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:190
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/network_layout.py:199
|
||||
@@ -3419,11 +3433,26 @@ msgstr "適用於「{}」"
|
||||
msgid "for private branch updates"
|
||||
msgstr "用於私有分支更新"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:99
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:116
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"force\n"
|
||||
"offroad"
|
||||
msgstr ""
|
||||
"強制\n"
|
||||
"駐車"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/display.py:28
|
||||
#, python-brace-format
|
||||
msgid "force mici UI"
|
||||
msgstr "強制使用 mici 介面"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/device.py:111
|
||||
#, python-brace-format
|
||||
msgid "force offroad"
|
||||
msgstr "強制駐車"
|
||||
|
||||
#: openpilot/selfdrive/ui/mici/layouts/settings/network/wifi_ui.py:247
|
||||
#, python-brace-format
|
||||
msgid "forgetting..."
|
||||
|
||||
@@ -13,7 +13,7 @@ from iqpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
_FPS_OVERRIDE = os.getenv("FPS")
|
||||
UI_OFFROAD_FPS = int(os.getenv("UI_OFFROAD_FPS", _FPS_OVERRIDE or "60"))
|
||||
UI_ONROAD_FPS = int(os.getenv("UI_ONROAD_FPS", _FPS_OVERRIDE or "20"))
|
||||
UI_ONROAD_FPS = int(os.getenv("UI_ONROAD_FPS", _FPS_OVERRIDE or "60"))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -83,6 +83,7 @@ class WebRTCBaseStream(abc.ABC):
|
||||
self._audio_track_state: List[Tuple[Track, Any, RtpPacketizationConfig]] = []
|
||||
self._receiver_reports: Dict[str, RtcpReceiverReport] = {}
|
||||
self._receiver_report_tracks: Dict[str, Tuple[Track, int]] = {}
|
||||
self._negotiated_tracks: Dict[str, Track] = {}
|
||||
|
||||
self.incoming_media_ready_event = asyncio.Event()
|
||||
self.messaging_channel_ready_event = asyncio.Event()
|
||||
@@ -206,7 +207,11 @@ class WebRTCBaseStream(abc.ABC):
|
||||
def _add_producer_tracks(self, remote_sdp: Optional[str] = None):
|
||||
for track in self.outgoing_video_tracks:
|
||||
media, ssrc, payload_type, cname = self._make_video_media(track, remote_sdp or "")
|
||||
rtc_track = self.peer_connection.add_track(media)
|
||||
rtc_track = self._negotiated_tracks.get(media.mid())
|
||||
if rtc_track is not None:
|
||||
rtc_track.set_description(media)
|
||||
else:
|
||||
rtc_track = self.peer_connection.add_track(media)
|
||||
|
||||
rtp_config = RtpPacketizationConfig(ssrc, cname, payload_type, H264RtpPacketizer.CLOCK_RATE)
|
||||
rtp_config.start_timestamp = random.randint(0, 0xFFFFFFFF)
|
||||
@@ -232,6 +237,8 @@ class WebRTCBaseStream(abc.ABC):
|
||||
# same MID, which leaves libdatachannel stuck in signalling/ICE negotiation.
|
||||
rtc_track = self.incoming_audio_tracks[0]
|
||||
negotiated_media = rtc_track.description()
|
||||
negotiated_media.clear_ssrcs()
|
||||
negotiated_media.remove_attribute("msid")
|
||||
negotiated_media.add_ssrc(ssrc, cname, "audio", "audio")
|
||||
rtc_track.set_description(negotiated_media)
|
||||
else:
|
||||
@@ -276,6 +283,7 @@ class WebRTCBaseStream(abc.ABC):
|
||||
|
||||
def _on_incoming_track(self, track: Track):
|
||||
self._log_debug("got track: %s", track.mid())
|
||||
self._negotiated_tracks[track.mid()] = track
|
||||
media_type = track.description().type()
|
||||
if media_type == "audio":
|
||||
if self.expected_incoming_audio:
|
||||
@@ -478,6 +486,7 @@ class WebRTCBaseStream(abc.ABC):
|
||||
self._audio_track_state.clear()
|
||||
self._receiver_reports.clear()
|
||||
self._receiver_report_tracks.clear()
|
||||
self._negotiated_tracks.clear()
|
||||
|
||||
@abc.abstractmethod
|
||||
async def start(self) -> RTCSessionDescription:
|
||||
|
||||
@@ -19,8 +19,17 @@ async def test_native_video_audio_and_data_channel():
|
||||
|
||||
async def connect(offer):
|
||||
nonlocal answer_session
|
||||
answer_session = StreamSession(offer.sdp, offer.video, [], [], [], debug_mode=True)
|
||||
browser_offer = offer.sdp.replace("profile-level-id=42e01f", "profile-level-id=640c1f", 1)
|
||||
audio_offset = browser_offer.index("m=audio")
|
||||
browser_offer = browser_offer[:audio_offset] + browser_offer[audio_offset:].replace(
|
||||
"a=recvonly",
|
||||
"a=sendrecv\r\na=msid:ios-microphone ios-audio-track\r\na=ssrc:123456 cname:ios-audio\r\na=ssrc:123456 msid:ios-microphone ios-audio-track",
|
||||
1,
|
||||
)
|
||||
answer_session = StreamSession(browser_offer, offer.video, [], [], [], debug_mode=True)
|
||||
answer = await answer_session.get_answer()
|
||||
assert not any(line.startswith("m=video 0 ") for line in answer.sdp.splitlines())
|
||||
assert answer_session.stream._track_state[0][0] is answer_session.stream._negotiated_tracks["road"]
|
||||
answer_session.start()
|
||||
return RTCSessionDescription(answer.sdp, answer.type)
|
||||
|
||||
@@ -56,9 +65,16 @@ async def test_native_duplex_audio_negotiation():
|
||||
|
||||
async def connect(offer):
|
||||
nonlocal answer_session
|
||||
sendrecv_offer = offer.sdp.replace("a=recvonly", "a=sendrecv", 1)
|
||||
sendrecv_offer = offer.sdp.replace(
|
||||
"a=recvonly",
|
||||
"a=sendrecv\r\na=msid:ios-microphone ios-audio-track\r\na=ssrc:123456 cname:ios-audio\r\na=ssrc:123456 msid:ios-microphone ios-audio-track",
|
||||
1,
|
||||
)
|
||||
answer_session = StreamSession(sendrecv_offer, offer.video, [], [], [], debug_mode=True)
|
||||
answer = await answer_session.get_answer()
|
||||
assert "ios-microphone" not in answer.sdp
|
||||
assert "ios-audio-track" not in answer.sdp
|
||||
assert answer.sdp.count("a=msid:audio audio") == 1
|
||||
answer_session.start()
|
||||
return RTCSessionDescription(answer.sdp, answer.type)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user