IQ.Pilot Release Commit @ 6d177d4
This commit is contained in:
@@ -1,11 +1,6 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
# DISCLAIMER: This code is intended principally for development and debugging purposes.
|
||||
# Although it provides a standalone entry point to the program, users should refer
|
||||
# to the actual implementations for consumption. Usage outside of development scenarios
|
||||
# is not advised and could lead to unpredictable results.
|
||||
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
@@ -13,26 +8,27 @@ from openpilot.common.realtime import Ratekeeper, config_realtime_process
|
||||
from openpilot.iqpilot.iq_maps.road_data import debug_road_data
|
||||
from openpilot.iqpilot.iq_maps.road_data.iq_road_layer import IQRoadLayer
|
||||
|
||||
ROAD_LAYER_HZ = 1
|
||||
ROAD_LAYER_CORES = [0, 1, 2, 3]
|
||||
|
||||
def excepthook(args):
|
||||
debug_road_data(f'IQ maps threading exception:\n{args}')
|
||||
|
||||
def _log_thread_exception(args) -> None:
|
||||
debug_road_data(f"IQ maps threading exception:\n{args}")
|
||||
traceback.print_exception(args.exc_type, args.exc_value, args.exc_traceback)
|
||||
|
||||
|
||||
def live_map_data_iq_thread():
|
||||
config_realtime_process([0, 1, 2, 3], 5)
|
||||
|
||||
live_map_iq = IQRoadLayer()
|
||||
rk = Ratekeeper(1, print_delay_threshold=None)
|
||||
|
||||
def run() -> None:
|
||||
config_realtime_process(ROAD_LAYER_CORES, 5)
|
||||
layer = IQRoadLayer()
|
||||
rk = Ratekeeper(ROAD_LAYER_HZ, print_delay_threshold=None)
|
||||
while True:
|
||||
live_map_iq.step()
|
||||
layer.step()
|
||||
rk.keep_time()
|
||||
|
||||
|
||||
def main():
|
||||
threading.excepthook = excepthook
|
||||
live_map_data_iq_thread()
|
||||
def main() -> None:
|
||||
threading.excepthook = _log_thread_exception
|
||||
run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
|
||||
Installs the `mapd` binary by Jacob Pfeifer (github.com/pfeiferj/mapd) — the binary is his work.
|
||||
"""
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
import time
|
||||
import traceback
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from urllib.request import urlopen
|
||||
|
||||
import requests
|
||||
|
||||
from cereal import messaging
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.common.spinner import Spinner
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.version import is_prebuilt
|
||||
from openpilot.iqpilot.iq_maps import VENDOR_MAPD_BIN_DIR, VENDOR_MAPD_PATH
|
||||
import openpilot.system.sentry as sentry
|
||||
@@ -23,17 +27,28 @@ import openpilot.system.sentry as sentry
|
||||
VENDOR_RELEASE_TAG = "v2.0.6"
|
||||
VENDOR_RELEASE_URL = f"https://github.com/pfeiferj/mapd/releases/download/{VENDOR_RELEASE_TAG}/mapd"
|
||||
|
||||
_VERSION_PARAM = "MapdVersion"
|
||||
_HASH_FILE = os.path.join(BASEDIR, "iqpilot", "iq_maps", "tests", "mapd_hash")
|
||||
_DOWNLOAD_TIMEOUT_S = 60
|
||||
_MAX_DOWNLOAD_TRIES = 5
|
||||
|
||||
|
||||
def get_file_hash(path: str) -> str:
|
||||
"""Hex SHA-256 of a file's contents."""
|
||||
with open(path, "rb") as handle:
|
||||
return hashlib.file_digest(handle, "sha256").hexdigest()
|
||||
|
||||
|
||||
def stamp_vendor_version(version: str, params: Params | None = None) -> None:
|
||||
if params is None:
|
||||
params = Params()
|
||||
|
||||
params.put("MapdVersion", version)
|
||||
(params or Params()).put(_VERSION_PARAM, version)
|
||||
|
||||
|
||||
def _expected_vendor_hash_path() -> str:
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
return os.path.join(BASEDIR, "iqpilot", "iq_maps", "tests", "mapd_hash")
|
||||
def _pinned_hash() -> str:
|
||||
try:
|
||||
with open(_HASH_FILE) as f:
|
||||
return f.read().strip()
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
|
||||
class VendorMapdInstaller:
|
||||
@@ -41,98 +56,74 @@ class VendorMapdInstaller:
|
||||
self._spinner = spinner_ref
|
||||
self._params = Params()
|
||||
|
||||
def fetch(self) -> None:
|
||||
self.ensure_directories_exist()
|
||||
self._download_file()
|
||||
stamp_vendor_version(VENDOR_RELEASE_TAG, self._params)
|
||||
def get_installed_version(self) -> str:
|
||||
return str(self._params.get(_VERSION_PARAM) or "")
|
||||
|
||||
def check_and_download(self) -> None:
|
||||
if self.download_needed():
|
||||
self.fetch()
|
||||
@staticmethod
|
||||
def ensure_directories_exist() -> None:
|
||||
for d in (Paths.mapd_root(), VENDOR_MAPD_BIN_DIR):
|
||||
os.makedirs(d, exist_ok=True)
|
||||
|
||||
def download_needed(self) -> bool:
|
||||
if not os.path.exists(VENDOR_MAPD_PATH):
|
||||
return True
|
||||
if self.get_installed_version() != VENDOR_RELEASE_TAG:
|
||||
return True
|
||||
return not self._binary_hash_matches()
|
||||
|
||||
@staticmethod
|
||||
def _binary_hash_matches() -> bool:
|
||||
pinned = _pinned_hash()
|
||||
if not pinned:
|
||||
return False
|
||||
try:
|
||||
hash_path = _expected_vendor_hash_path()
|
||||
with open(hash_path) as f:
|
||||
expected = f.read().strip()
|
||||
except Exception:
|
||||
return True
|
||||
if not expected:
|
||||
return True
|
||||
try:
|
||||
return get_file_hash(VENDOR_MAPD_PATH) == expected
|
||||
except Exception:
|
||||
return get_file_hash(VENDOR_MAPD_PATH) != pinned
|
||||
except OSError:
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def ensure_directories_exist() -> None:
|
||||
if not os.path.exists(Paths.mapd_root()):
|
||||
os.makedirs(Paths.mapd_root())
|
||||
if not os.path.exists(VENDOR_MAPD_BIN_DIR):
|
||||
os.makedirs(VENDOR_MAPD_BIN_DIR)
|
||||
def check_and_download(self) -> None:
|
||||
if self.download_needed():
|
||||
self.fetch()
|
||||
|
||||
@staticmethod
|
||||
def _safe_write_and_set_executable(file_path: Path, content: bytes) -> None:
|
||||
with open(file_path, 'wb') as output:
|
||||
output.write(content)
|
||||
output.flush()
|
||||
os.fsync(output.fileno())
|
||||
current_permissions = stat.S_IMODE(os.lstat(file_path).st_mode)
|
||||
os.chmod(file_path, current_permissions | stat.S_IEXEC)
|
||||
def fetch(self) -> None:
|
||||
self.ensure_directories_exist()
|
||||
if self._pull_binary():
|
||||
stamp_vendor_version(VENDOR_RELEASE_TAG, self._params)
|
||||
|
||||
def _download_file(self, num_retries=5) -> None:
|
||||
temp_file = Path(VENDOR_MAPD_PATH + ".tmp")
|
||||
download_timeout = 60
|
||||
for cnt in range(num_retries):
|
||||
def _pull_binary(self) -> bool:
|
||||
scratch = Path(f"{VENDOR_MAPD_PATH}.tmp")
|
||||
for attempt in range(1, _MAX_DOWNLOAD_TRIES + 1):
|
||||
try:
|
||||
response = requests.get(VENDOR_RELEASE_URL, stream=True, timeout=download_timeout)
|
||||
response.raise_for_status()
|
||||
self._safe_write_and_set_executable(temp_file, response.content)
|
||||
temp_file.replace(VENDOR_MAPD_PATH)
|
||||
return
|
||||
except requests.exceptions.ReadTimeout:
|
||||
self._spinner.update(f"ReadTimeout caught. Timeout is [{download_timeout}]. Retrying download... [{cnt}]")
|
||||
time.sleep(0.5)
|
||||
resp = requests.get(VENDOR_RELEASE_URL, stream=True, timeout=_DOWNLOAD_TIMEOUT_S)
|
||||
resp.raise_for_status()
|
||||
with open(scratch, "wb") as out:
|
||||
out.write(resp.content)
|
||||
out.flush()
|
||||
os.fsync(out.fileno())
|
||||
mode = stat.S_IMODE(os.lstat(scratch).st_mode)
|
||||
os.chmod(scratch, mode | stat.S_IEXEC)
|
||||
scratch.replace(VENDOR_MAPD_PATH)
|
||||
return True
|
||||
except requests.exceptions.RequestException as e:
|
||||
self._spinner.update(f"RequestException caught: {e}. Retrying download... [{cnt}]")
|
||||
self._spinner.update(f"mapd download failed ({e}); retry {attempt}/{_MAX_DOWNLOAD_TRIES}")
|
||||
time.sleep(0.5)
|
||||
|
||||
# Delete temp file if the process was not successful.
|
||||
if temp_file.exists():
|
||||
temp_file.unlink()
|
||||
logging.error("Failed to download file after all retries")
|
||||
|
||||
def get_installed_version(self) -> str:
|
||||
return str(self._params.get("MapdVersion") or "")
|
||||
scratch.unlink(missing_ok=True)
|
||||
logging.error("mapd binary download failed after %d attempts", _MAX_DOWNLOAD_TRIES)
|
||||
return False
|
||||
|
||||
def wait_for_internet_connection(self, return_on_failure: bool = False) -> bool:
|
||||
max_retries = 10
|
||||
for retries in range(max_retries + 1):
|
||||
self._spinner.update(f"Waiting for internet connection... [{retries}/{max_retries}]")
|
||||
attempts = 10
|
||||
for i in range(attempts + 1):
|
||||
self._spinner.update(f"Waiting for internet connection... [{i}/{attempts}]")
|
||||
time.sleep(2)
|
||||
try:
|
||||
_ = urlopen('https://sentry.io', timeout=10)
|
||||
urlopen("https://sentry.io", timeout=10)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f'Wait for internet failed: {e}')
|
||||
if return_on_failure and retries == max_retries:
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"Wait for internet failed: {e}")
|
||||
if return_on_failure and i == attempts:
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def non_prebuilt_install(self) -> None:
|
||||
sm = messaging.SubMaster(['deviceState'])
|
||||
metered = sm['deviceState'].networkMetered
|
||||
|
||||
if metered:
|
||||
sm = messaging.SubMaster(["deviceState"])
|
||||
if sm["deviceState"].networkMetered:
|
||||
self._spinner.update("Can't proceed with mapd install since network is metered!")
|
||||
time.sleep(5)
|
||||
return
|
||||
@@ -149,14 +140,11 @@ class VendorMapdInstaller:
|
||||
time.sleep(0.1)
|
||||
self.check_and_download()
|
||||
self._spinner.close()
|
||||
|
||||
except Exception:
|
||||
except Exception: # noqa: BLE001
|
||||
for i in range(6):
|
||||
self._spinner.update("Failed to download OSM maps won't work until properly downloaded!" +
|
||||
"Try again manually rebooting. " +
|
||||
f"Boot will continue in {5 - i}s...")
|
||||
self._spinner.update("Failed to download OSM maps won't work until properly downloaded!"
|
||||
f"Try again manually rebooting. Boot will continue in {5 - i}s...")
|
||||
time.sleep(1)
|
||||
|
||||
sentry.init(sentry.SentryProject.SELFDRIVE)
|
||||
traceback.print_exc()
|
||||
sentry.capture_exception()
|
||||
@@ -164,18 +152,12 @@ class VendorMapdInstaller:
|
||||
|
||||
if __name__ == "__main__":
|
||||
spinner = Spinner()
|
||||
install_manager = VendorMapdInstaller(spinner)
|
||||
install_manager.ensure_directories_exist()
|
||||
installer = VendorMapdInstaller(spinner)
|
||||
installer.ensure_directories_exist()
|
||||
if is_prebuilt():
|
||||
debug_msg = f"[DEBUG] This is prebuilt, no vendor mapd install required. VERSION: [{VENDOR_RELEASE_TAG}], Param [{install_manager.get_installed_version()}]"
|
||||
spinner.update(debug_msg)
|
||||
spinner.update(f"[DEBUG] Prebuilt build; no vendor mapd install required. "
|
||||
f"VERSION: [{VENDOR_RELEASE_TAG}], Param [{installer.get_installed_version()}]")
|
||||
stamp_vendor_version(VENDOR_RELEASE_TAG)
|
||||
else:
|
||||
spinner.update(f"Checking if vendor mapd is installed and valid. Prebuilt [{is_prebuilt()}]")
|
||||
install_manager.non_prebuilt_install()
|
||||
|
||||
|
||||
def get_file_hash(path: str) -> str:
|
||||
"""Hex SHA-256 of a file's contents."""
|
||||
with open(path, "rb") as handle:
|
||||
return hashlib.file_digest(handle, "sha256").hexdigest()
|
||||
installer.non_prebuilt_install()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# openpilot model I/O constants (comma.ai, MIT — see LICENSE)
|
||||
import numpy as np
|
||||
|
||||
|
||||
@@ -14,7 +15,7 @@ class SplitModelConstants:
|
||||
LEAD_T_OFFSETS = [0., 2., 4.]
|
||||
META_T_IDXS = [2., 4., 6., 8., 10.]
|
||||
|
||||
# model inputs constants
|
||||
# split-model temporal / history run parameters
|
||||
MODEL_FREQ = 20
|
||||
HISTORY_FREQ = 5
|
||||
HISTORY_LEN_SECONDS = 5
|
||||
|
||||
Reference in New Issue
Block a user