mirror of
https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot.git
synced 2026-10-27 21:13:45 +08:00
IQ.Pilot Release Commit @ e99f304
This commit is contained in:
@@ -68,6 +68,7 @@ compare_runtime*.html
|
||||
|
||||
iqpilot/selfdrive/pandad/pandad
|
||||
iqpilot/selfdrive/pandad/tests/test_panda_safety
|
||||
iqpilot/selfdrive/pandad/tests/test_ignition_hold
|
||||
iqpilot/cereal/services.h
|
||||
iqpilot/cereal/gen
|
||||
iqpilot/cereal/messaging/bridge
|
||||
@@ -180,3 +181,5 @@ iqpilot/tools/iqpilot/big_hud_preview.py
|
||||
artifacts/monrovia_runtime_packages/
|
||||
artifacts/iqpilot_*_private/consumer.challenge
|
||||
/docs/
|
||||
/dist/
|
||||
nuitka-crash-report*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
IQ.Lvbs License v1.8.2
|
||||
IQ.Lvbs License v1.8.3
|
||||
|
||||
Copyright (c) 2026 IQ.Lvbs LLC, a part of Project Teal Lvbs Inc. All Rights Reserved.
|
||||
|
||||
@@ -42,7 +42,8 @@ You may NOT:
|
||||
5. Sublicense, sell, or transfer rights to the Software.
|
||||
|
||||
6. Use the Software and/or its source code to compete with or create a
|
||||
substantially similar product.
|
||||
substantially similar product that is not a personal non-commercial fork of IQ.Pilot that complies with
|
||||
all other subsections of this license.
|
||||
|
||||
7. Use the Software in closed source software not licensed by IQ.Lvbs LLC.
|
||||
|
||||
@@ -54,9 +55,8 @@ OWNERSHIP
|
||||
|
||||
All rights, title, and interest in the Software remain exclusively with the
|
||||
Copyright Holder. Any modifications, improvements, or derivative works you
|
||||
create based on the Software are owned by the Copyright Holder. By
|
||||
contributing modifications, you irrevocably assign all rights to the
|
||||
Copyright Holder.
|
||||
create based on the Software are owned by you (licensing of that work is in no manner the liability of IQ Lvbs or its associates).
|
||||
Any derivative in which IQ.Pilot source code is used and modified shall remain the property of IQ Lvbs with the exception of the modifications you have added.
|
||||
|
||||
PROPRIETARY COMPONENTS
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
76a146832ea70711033c567d35af66df13221c2e
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"python/_iqclosure/_vendored.json": {
|
||||
"mode": 420,
|
||||
"sha256": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570",
|
||||
"size": 3
|
||||
},
|
||||
"python/iqpilot_private/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/iqlvbs/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "16ce15884bfd765c17c05da0e2474aa1e7ddd8fcf68925b2169e19042235b4b6",
|
||||
"size": 469760
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "d5afc804c71ebd0c690035cbec639f5f07112b3a7080615e6430b27a1739984f",
|
||||
"size": 69608
|
||||
},
|
||||
"runtime": {
|
||||
"entries": {
|
||||
"iqpilot_private.konn3kt.iqlvbs.alc": {
|
||||
"path": "python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so",
|
||||
"type": "python-module"
|
||||
},
|
||||
"iqpilot_private.konn3kt.iqlvbs.vehicle_state": {
|
||||
"path": "python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so",
|
||||
"type": "python-module"
|
||||
}
|
||||
}
|
||||
},
|
||||
"signatures": {
|
||||
"python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": "3OWEWh1t7VakRznr0JljvzvODcR9GG1lU9Fx0Bu+J+m7u+U8JdOeoBSjy0DQSKPXpZ4jgi4ieVGXihJME5AsCg==",
|
||||
"python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so": "XzMSXupOCeoHX/pCV7X1kuDRw02qrCxJuuwEmSC50h9jy4ySWHk9vflvwRW3u+A6hZ9bljsK6wy0oFXZNoXcAg=="
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
zfDjghpCSIizLkZR7YlPtvZQirhuMbIYJahB/pGnNaffGIWuXGM7NPoTol0XxKdoMYvCDCu7Kj5YcAdfYoFoBQ==
|
||||
@@ -1 +0,0 @@
|
||||
[]
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,8 +1,94 @@
|
||||
{
|
||||
"bundle_version": 1792551365,
|
||||
"python/_iqclosure/_vendored.json": {
|
||||
"mode": 420,
|
||||
"sha256": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570",
|
||||
"size": 3
|
||||
"sha256": "ca67a05945042c0f65f1b48482d9f8effa71448326f7d6ef799ec91ead96a8c9",
|
||||
"size": 454
|
||||
},
|
||||
"python/_iqclosure/iqpilot/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/gpio.py": {
|
||||
"mode": 420,
|
||||
"sha256": "84edc2cba235b774293cbb1c52c6da74856425ff8ad99198997e23a05e784bb7",
|
||||
"size": 2166
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/i2c.py": {
|
||||
"mode": 420,
|
||||
"sha256": "4af8f9da7504a548d15f18b91d71c1379cb7c05c84be031448764cc49920ce68",
|
||||
"size": 2449
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/time_helpers.py": {
|
||||
"mode": 420,
|
||||
"sha256": "c14cda1f84149d5ffa63a2adec5ca24126f77c1412a78412e5e3e68a6fd12bcf",
|
||||
"size": 474
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/utils.py": {
|
||||
"mode": 420,
|
||||
"sha256": "c39b89a1ab34511dfa5f9efac783fde0e6d616963a416cf472bd1f604b57f84d",
|
||||
"size": 5892
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "889391bffca493e74bfc108cae2976a53b644caca13ab600061fd29e9622c627",
|
||||
"size": 695
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/base.py": {
|
||||
"mode": 420,
|
||||
"sha256": "a56293f7c23e503ff62d0d3ed39f1831b5544a65b37378165934361a5ddcd304",
|
||||
"size": 4966
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/pc/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/pc/hardware.py": {
|
||||
"mode": 420,
|
||||
"sha256": "2520895597c375ae570bd04d5f189e874c89d642f3f07bdbeb20388cb6691433",
|
||||
"size": 258
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/amplifier.py": {
|
||||
"mode": 420,
|
||||
"sha256": "38e521356c56ac85f35543b9457d36639e55ed2ac916f30ea3b86948e806541f",
|
||||
"size": 8016
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/hardware.py": {
|
||||
"mode": 420,
|
||||
"sha256": "da5ecb280f7af14169903260dc941e3bfac033c93b184cb0abea0f1494c34582",
|
||||
"size": 25731
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/iwlist.py": {
|
||||
"mode": 420,
|
||||
"sha256": "d22858982705531732d5a20a45a73795496e5bbd9e23028c24609a7e58ac7aed",
|
||||
"size": 890
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/lpa.py": {
|
||||
"mode": 420,
|
||||
"sha256": "2bffb84aef35892c4788e80b6d937bf2eebd15e226b72a82c4f39217667c1ca0",
|
||||
"size": 53692
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/pins.py": {
|
||||
"mode": 420,
|
||||
"sha256": "b57ba4262e6a6a79f2db15c27356acf3e54f579a7edd13b483b2e16d140aca6d",
|
||||
"size": 678
|
||||
},
|
||||
"python/iqpilot_private/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -20,9 +106,9 @@
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "9b48803b46906943d7cb10b7e4b4f38c880f06a5c281f254a89e2254965b025f",
|
||||
"size": 68080
|
||||
"mode": 416,
|
||||
"sha256": "4d988d95e9de50403be4ad3f733fb4a49ee153f604736736f2fa20a44e419748",
|
||||
"size": 1065272
|
||||
},
|
||||
"runtime": {
|
||||
"entries": {
|
||||
@@ -33,6 +119,6 @@
|
||||
}
|
||||
},
|
||||
"signatures": {
|
||||
"python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so": "RwstZkEGexV6fia/R9BRk+6CQ9Q6SGJopSJf04UpMpaw5fRtoPjLOrDFOt9kU8GPmCmW40gaXhJK+rimnBYnBw=="
|
||||
"python/iqpilot_private/konn3kt/iqlvbs/iqlvbs_commander.cpython-312-aarch64-linux-gnu.so": "LZv8eAULL242KYGIwYc2uAfm3U2idwa3HW09LPfpVkPUK7o+sNd7RHNjUINR+tq/7QyQao9Ev8uUZCbRNxWUDA=="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
TV78NXdEYQiabAzLIqS2lsZbp5bO0y4EPGwLRUMyKeEH2BStZiFfpNPvlqSn5HGI+RjmTq+p5rRUDMNofbZGBw==
|
||||
+0m3OFfYq3omyTvGD2hNN6MrCbY6lxnLEwQal9thza+Baxp6D5Wi38Def/7buznDfmIgJ5QenNBfP42H1NgnAg==
|
||||
|
||||
@@ -1 +1,15 @@
|
||||
[]
|
||||
[
|
||||
"iqpilot.common.gpio",
|
||||
"iqpilot.common.i2c",
|
||||
"iqpilot.common.time_helpers",
|
||||
"iqpilot.common.utils",
|
||||
"iqpilot.system.hardware",
|
||||
"iqpilot.system.hardware.base",
|
||||
"iqpilot.system.hardware.pc.hardware",
|
||||
"iqpilot.system.hardware.tici",
|
||||
"iqpilot.system.hardware.tici.amplifier",
|
||||
"iqpilot.system.hardware.tici.hardware",
|
||||
"iqpilot.system.hardware.tici.iwlist",
|
||||
"iqpilot.system.hardware.tici.lpa",
|
||||
"iqpilot.system.hardware.tici.pins"
|
||||
]
|
||||
|
||||
-10
@@ -27,16 +27,6 @@ def gpio_read(pin: int) -> bool | None:
|
||||
|
||||
return val
|
||||
|
||||
def gpio_export(pin: int) -> None:
|
||||
if os.path.isdir(f"/sys/class/gpio/gpio{pin}"):
|
||||
return
|
||||
|
||||
try:
|
||||
with open("/sys/class/gpio/export", 'w') as f:
|
||||
f.write(str(pin))
|
||||
except Exception:
|
||||
print(f"Failed to export gpio {pin}")
|
||||
|
||||
@cache
|
||||
def get_irq_action(irq: int) -> list[str]:
|
||||
try:
|
||||
+27
-107
@@ -5,7 +5,6 @@ import contextlib
|
||||
import subprocess
|
||||
import time
|
||||
import functools
|
||||
from subprocess import Popen, PIPE, TimeoutExpired
|
||||
import zstandard as zstd
|
||||
|
||||
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
|
||||
@@ -32,24 +31,44 @@ class Timer:
|
||||
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
|
||||
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
|
||||
|
||||
_sudo_available: bool | None = None
|
||||
|
||||
|
||||
def sudo_write(val: str, path: str) -> None:
|
||||
global _sudo_available
|
||||
try:
|
||||
with open(path, 'w') as f:
|
||||
f.write(str(val))
|
||||
except PermissionError:
|
||||
os.system(f"sudo chmod a+w {path}")
|
||||
try:
|
||||
return
|
||||
except OSError:
|
||||
pass
|
||||
# AGNOS 4.9.9.2 removes owner root; a privileged boot step opens these nodes to the stack so a direct write normally works. Only probe a non-interactive escalation once (interactive sudo blocks with no tty, and sudo -n itself can stall), and never let its failure or timeout propagate.
|
||||
if _sudo_available is False:
|
||||
return
|
||||
try:
|
||||
_sudo_available = subprocess.run(["sudo", "-n", "chmod", "a+w", path], timeout=2, capture_output=True).returncode == 0
|
||||
if _sudo_available:
|
||||
with open(path, 'w') as f:
|
||||
f.write(str(val))
|
||||
except PermissionError:
|
||||
# fallback for debugfs files
|
||||
os.system(f"sudo su -c 'echo {val} > {path}'")
|
||||
except Exception:
|
||||
_sudo_available = False
|
||||
|
||||
|
||||
def sudo_read(path: str) -> str:
|
||||
global _sudo_available
|
||||
try:
|
||||
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
|
||||
with open(path, encoding='utf8') as f:
|
||||
return f.read().strip()
|
||||
except OSError:
|
||||
pass
|
||||
if _sudo_available is False:
|
||||
return ""
|
||||
try:
|
||||
result = subprocess.run(["sudo", "-n", "cat", path], timeout=2, capture_output=True, text=True)
|
||||
_sudo_available = result.returncode == 0
|
||||
return result.stdout.strip()
|
||||
except Exception:
|
||||
_sudo_available = False
|
||||
return ""
|
||||
|
||||
|
||||
@@ -153,105 +172,6 @@ def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> st
|
||||
return default
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def managed_proc(cmd: list[str], env: dict[str, str]):
|
||||
proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE)
|
||||
try:
|
||||
yield proc
|
||||
finally:
|
||||
if proc.poll() is None:
|
||||
proc.terminate()
|
||||
try:
|
||||
proc.wait(timeout=5)
|
||||
except TimeoutExpired:
|
||||
proc.kill()
|
||||
|
||||
|
||||
def tabulate(tabular_data, headers=(), tablefmt="simple", floatfmt="g", stralign="left", numalign=None):
|
||||
rows = [list(row) for row in tabular_data]
|
||||
|
||||
def fmt(val):
|
||||
if isinstance(val, str):
|
||||
return val
|
||||
if isinstance(val, (bool, int)):
|
||||
return str(val)
|
||||
try:
|
||||
return format(val, floatfmt)
|
||||
except (TypeError, ValueError):
|
||||
return str(val)
|
||||
|
||||
formatted = [[fmt(c) for c in row] for row in rows]
|
||||
hdrs = [str(h) for h in headers] if headers else None
|
||||
|
||||
ncols = max((len(r) for r in formatted), default=0)
|
||||
if hdrs:
|
||||
ncols = max(ncols, len(hdrs))
|
||||
if ncols == 0:
|
||||
return ""
|
||||
|
||||
for r in formatted:
|
||||
r.extend([""] * (ncols - len(r)))
|
||||
if hdrs:
|
||||
hdrs.extend([""] * (ncols - len(hdrs)))
|
||||
|
||||
widths = [0] * ncols
|
||||
if hdrs:
|
||||
for i in range(ncols):
|
||||
widths[i] = len(hdrs[i])
|
||||
for row in formatted:
|
||||
for i in range(ncols):
|
||||
widths[i] = max(widths[i], max(len(ln) for ln in row[i].split('\n')))
|
||||
|
||||
def _align(s, w):
|
||||
if stralign == "center":
|
||||
return s.center(w)
|
||||
return s.ljust(w)
|
||||
|
||||
if tablefmt == "html":
|
||||
parts = ["<table>"]
|
||||
if hdrs:
|
||||
parts.append("<thead>")
|
||||
parts.append("<tr>" + "".join(f"<th>{h}</th>" for h in hdrs) + "</tr>")
|
||||
parts.append("</thead>")
|
||||
parts.append("<tbody>")
|
||||
for row in formatted:
|
||||
parts.append("<tr>" + "".join(f"<td>{c}</td>" for c in row) + "</tr>")
|
||||
parts.append("</tbody>")
|
||||
parts.append("</table>")
|
||||
return "\n".join(parts)
|
||||
|
||||
if tablefmt == "simple_grid":
|
||||
def _sep(left, mid, right):
|
||||
return left + mid.join("─" * (w + 2) for w in widths) + right
|
||||
|
||||
top, mid_sep, bot = _sep("┌", "┬", "┐"), _sep("├", "┼", "┤"), _sep("└", "┴", "┘")
|
||||
|
||||
def _fmt_row(cells):
|
||||
split = [c.split('\n') for c in cells]
|
||||
nlines = max(len(s) for s in split)
|
||||
for s in split:
|
||||
s.extend([""] * (nlines - len(s)))
|
||||
return ["│" + "│".join(f" {_align(split[i][li], widths[i])} " for i in range(ncols)) + "│" for li in range(nlines)]
|
||||
|
||||
lines = [top]
|
||||
if hdrs:
|
||||
lines.extend(_fmt_row(hdrs))
|
||||
lines.append(mid_sep)
|
||||
for ri, row in enumerate(formatted):
|
||||
lines.extend(_fmt_row(row))
|
||||
lines.append(mid_sep if ri < len(formatted) - 1 else bot)
|
||||
return "\n".join(lines)
|
||||
|
||||
gap = " "
|
||||
lines = []
|
||||
if hdrs:
|
||||
lines.append(gap.join(h.ljust(w) for h, w in zip(hdrs, widths, strict=True)))
|
||||
lines.append(gap.join("-" * w for w in widths))
|
||||
for row in formatted:
|
||||
lines.append(gap.join(_align(row[i], widths[i]) for i in range(ncols)))
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def retry(attempts=3, delay=1.0, ignore_failure=False):
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
+21
-5
@@ -125,16 +125,32 @@ class Amplifier:
|
||||
print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}")
|
||||
|
||||
def set_configs(self, configs: list[AmpConfig]) -> bool:
|
||||
import threading
|
||||
|
||||
tries = 15
|
||||
backoff = 0.
|
||||
for i in range(tries):
|
||||
try:
|
||||
self._set_configs(configs)
|
||||
return True
|
||||
except OSError:
|
||||
result = [None]
|
||||
error = [None]
|
||||
def _worker():
|
||||
try:
|
||||
self._set_configs(configs)
|
||||
result[0] = True
|
||||
except OSError as e:
|
||||
error[0] = e
|
||||
t = threading.Thread(target=_worker, daemon=True)
|
||||
t.start()
|
||||
t.join(timeout=2)
|
||||
if t.is_alive():
|
||||
backoff += 0.1
|
||||
time.sleep(backoff)
|
||||
print(f"Failed to set amp config, {tries - i - 1} retries left")
|
||||
print(f"Failed to set amp config (timeout), {tries - i - 1} retries left")
|
||||
continue
|
||||
if result[0]:
|
||||
return True
|
||||
backoff += 0.1
|
||||
time.sleep(backoff)
|
||||
print(f"Failed to set amp config, {tries - i - 1} retries left")
|
||||
return False
|
||||
|
||||
def set_global_shutdown(self, amp_disabled: bool) -> bool:
|
||||
+9
-7
@@ -469,8 +469,10 @@ class Tici(HardwareBase):
|
||||
if self.amplifier is not None:
|
||||
self.amplifier.initialize_configuration(self.get_device_type())
|
||||
|
||||
# Allow hardwared to write engagement status to kmsg
|
||||
os.system("sudo chmod a+w /dev/kmsg")
|
||||
try:
|
||||
subprocess.run(["sudo", "-n", "chmod", "a+w", "/dev/kmsg"], timeout=2, capture_output=True, check=False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL
|
||||
gpio_init(GPIO.SOM_ST_IO, True)
|
||||
@@ -514,10 +516,10 @@ class Tici(HardwareBase):
|
||||
affine_irq(3, "xhci-hcd:usb1")
|
||||
try:
|
||||
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
|
||||
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
|
||||
subprocess.call(["sudo", "taskset", "-pc", "3", pid], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except subprocess.CalledProcessException as e:
|
||||
print(str(e))
|
||||
subprocess.run(["sudo", "-n", "chrt", "-f", "-p", "1", pid], timeout=2, capture_output=True, check=False)
|
||||
subprocess.run(["sudo", "-n", "taskset", "-pc", "3", pid], timeout=2, capture_output=True, check=False)
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
def configure_modem(self):
|
||||
from iqpilot.common.params import Params
|
||||
@@ -557,7 +559,7 @@ class Tici(HardwareBase):
|
||||
if self.get_device_type() in ("tici", "tizi"):
|
||||
if initial_eps_apn:
|
||||
subprocess.call(["mmcli", "-m", "any", f'--3gpp-set-initial-eps-bearer-settings=apn={initial_eps_apn}'])
|
||||
else:
|
||||
elif is_comma_profile:
|
||||
subprocess.call(["mmcli", "-m", "any", '--3gpp-set-initial-eps-bearer-settings=apn='])
|
||||
|
||||
cmds += [
|
||||
Executable → Regular
BIN
Binary file not shown.
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"bundle_version": 1792550965,
|
||||
"python/_iqclosure/_vendored.json": {
|
||||
"mode": 420,
|
||||
"sha256": "74f679d0ffd3a8e3225496dec9ac060c5505503c94fa04acabd0de4539c57871",
|
||||
"size": 1072
|
||||
"sha256": "ae4c187fb594e87be0b64ff050e3ab39cff5dace25e8f5766e3f11be6736992f",
|
||||
"size": 1137
|
||||
},
|
||||
"python/_iqclosure/iqpilot/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -16,13 +17,18 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
"sha256": "1dd848ec6e867836a321a93f9ac218415c19528aa356a3e80969503285316b54",
|
||||
"size": 900
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/base.py": {
|
||||
"mode": 420,
|
||||
"sha256": "86b834e02e24a005c903f1ca059b1a572e37c17a87271726c90918cd04b6133b",
|
||||
"size": 3451
|
||||
"sha256": "5b312ec45e673d9337a89ea82a157152d1f64e5e48518807a592247787087609",
|
||||
"size": 4921
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/konn3kt.py": {
|
||||
"mode": 420,
|
||||
"sha256": "2282e19909079bfd77a089f480df5a229191c35f8523f005d20e2ea468a6b0bc",
|
||||
"size": 313
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/basedir.py": {
|
||||
"mode": 420,
|
||||
@@ -61,18 +67,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/utils.py": {
|
||||
"mode": 420,
|
||||
"sha256": "103fa6c24f8b963576c482a34d7ecb020168fd3e85156493ae4432ee3b7b2c06",
|
||||
"size": 5159
|
||||
},
|
||||
"python/_iqclosure/iqpilot/konn3kt/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/konn3kt/cloud_client.py": {
|
||||
"mode": 420,
|
||||
"sha256": "4d58e62f8d628a8570881f2b87c82c801b46928fbb84f8dab8a81a9b55ba4e15",
|
||||
"size": 493
|
||||
"sha256": "c39b89a1ab34511dfa5f9efac783fde0e6d616963a416cf472bd1f604b57f84d",
|
||||
"size": 5892
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -116,8 +112,13 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/helpers.py": {
|
||||
"mode": 420,
|
||||
"sha256": "2fad977c3816ff824c126a92605f866f3bc0aa989ed8397e818bed481a41192f",
|
||||
"size": 14051
|
||||
"sha256": "9628f88e71f7fae2c3d62dd17618367b3c7d12749cfe2a4737410288888cd3bd",
|
||||
"size": 15008
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/iqc.py": {
|
||||
"mode": 420,
|
||||
"sha256": "54ec8e50513edae74ddb1c28015868183cb0c103de48386292d0b7fd83bcbb44",
|
||||
"size": 391
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/speed_limit_input.py": {
|
||||
"mode": 420,
|
||||
@@ -166,13 +167,13 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/amplifier.py": {
|
||||
"mode": 420,
|
||||
"sha256": "98f46f8705259b00efae7274186b4e68201aad467a19de4f16d5f850d10ab0b5",
|
||||
"size": 7585
|
||||
"sha256": "38e521356c56ac85f35543b9457d36639e55ed2ac916f30ea3b86948e806541f",
|
||||
"size": 8016
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/hardware.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e8eae4e8a75e1751525471cf54a8555b7bfd7f08ad84bf2e75642b5745f222db",
|
||||
"size": 25601
|
||||
"sha256": "da5ecb280f7af14169903260dc941e3bfac033c93b184cb0abea0f1494c34582",
|
||||
"size": 25731
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/iwlist.py": {
|
||||
"mode": 420,
|
||||
@@ -204,43 +205,52 @@
|
||||
"sha256": "b6d70f9628b596bef9eb6509e829d9cf94b4635c6e46bb8febde4a067203a222",
|
||||
"size": 113
|
||||
},
|
||||
"python/iqpilot_private/context/aux_inputs.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 416,
|
||||
"sha256": "a6d54e9ef8773e31e28411012a447ef4880ed7f8d43751bb16666adceee5e0e0",
|
||||
"size": 1065336
|
||||
},
|
||||
"python/iqpilot_private/context/cot_decode.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "506f732da9133ba5fdf8ffcb5d7557b28de44b8b789a4994fb1bd9a7c2240641",
|
||||
"size": 138936
|
||||
"mode": 416,
|
||||
"sha256": "47e0652a7f87850cfc9cdc28ed88b18cd36adb4da00d5e5b4c2181df7ae09089",
|
||||
"size": 1130696
|
||||
},
|
||||
"python/iqpilot_private/context/drive_profile.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "33358560c99c961e172a1643fb4a0ac6048f90d85551c0dea595fc1793372199",
|
||||
"size": 202256
|
||||
"mode": 416,
|
||||
"sha256": "a5a4c36f9aa116467c10e4fe9f07b4fde58d98b97e0950a799b9ce1c83190a08",
|
||||
"size": 1130920
|
||||
},
|
||||
"python/iqpilot_private/context/feature_inputs.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "e23916b1c8ce0dd6fe28e07d381e8545c14ab9851cc3c501cc8f08e2002df6ee",
|
||||
"size": 135368
|
||||
"mode": 416,
|
||||
"sha256": "c518443db5595c542ec1dec089be97ee79d3a1e7e0f661dbf8d069cf243da402",
|
||||
"size": 1130872
|
||||
},
|
||||
"python/iqpilot_private/context/model_context.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "cf6a559498981b3bebb4e50ef276e8c6f0816db68534c43b3372f2d88fec9f7d",
|
||||
"size": 473440
|
||||
"mode": 416,
|
||||
"sha256": "c7bfa17d75afa08c0f85256b461c1dd0318fadf7ca422b704673613a8ba54949",
|
||||
"size": 1524504
|
||||
},
|
||||
"python/iqpilot_private/context/reasoning_relay.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "674bb7de81a76911f536cd684b384f384b4271bdedf0591ff18c574c7378d6c7",
|
||||
"size": 137560
|
||||
"mode": 416,
|
||||
"sha256": "b4bd7aa63d69c0db66731eff1fb668551bb5eea17f9d0a8baba01fbb1ef05ae8",
|
||||
"size": 1130728
|
||||
},
|
||||
"python/iqpilot_private/context/speed_limit_input.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "b6b8c578e98f584ee221d35e08233f4a96afee3d3d765bc8ce5229040626f9c9",
|
||||
"size": 135872
|
||||
"mode": 416,
|
||||
"sha256": "9737473c0727f844593bd539cc106aa9358e6b62288e4ceb7a44c78425fec08b",
|
||||
"size": 1065272
|
||||
},
|
||||
"python/iqpilot_private/context/temporal_state.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "a08e7b38bdd666bf2aa38e7528574f2bb3650cd8439edc9689b1e36b08fcf5c2",
|
||||
"size": 205456
|
||||
"mode": 416,
|
||||
"sha256": "87c0e5b83e1f53b1984aa9debe7e6f5f8a6432b6de6c4f5d82cef01e31eb5ed8",
|
||||
"size": 1130776
|
||||
},
|
||||
"runtime": {
|
||||
"entries": {
|
||||
"iqpilot_private.context.aux_inputs": {
|
||||
"path": "python/iqpilot_private/context/aux_inputs.cpython-312-aarch64-linux-gnu.so",
|
||||
"type": "python-module"
|
||||
},
|
||||
"iqpilot_private.context.cot_decode": {
|
||||
"path": "python/iqpilot_private/context/cot_decode.cpython-312-aarch64-linux-gnu.so",
|
||||
"type": "python-module"
|
||||
@@ -272,12 +282,13 @@
|
||||
}
|
||||
},
|
||||
"signatures": {
|
||||
"python/iqpilot_private/context/cot_decode.cpython-312-aarch64-linux-gnu.so": "3XxFsaC/nx8fXtDtpWs+7oPABYmwlIEj1Ex30U8kTDzguB6P5RJVmkFbstAiWOnVCYfLT47sMlvHeU3VTHehDA==",
|
||||
"python/iqpilot_private/context/drive_profile.cpython-312-aarch64-linux-gnu.so": "9SRCLPJAzPMXMJRyxmfzIR3B2uTfpRm4uzUi1UBnSufzR+ppaRxfCtLestlXWDf2nkQiiaHjF3JdPv3VhjjmBg==",
|
||||
"python/iqpilot_private/context/feature_inputs.cpython-312-aarch64-linux-gnu.so": "KdG3SA295ptxX5WcOmdjeHUkE+f+DTRkPGcJFEXgLMPPMWSgo8aVYILT9z46JTUHd+va82ijRIAJZtJHE+wqAg==",
|
||||
"python/iqpilot_private/context/model_context.cpython-312-aarch64-linux-gnu.so": "yNV0YdFkYGTRyiBFwejPolZHv5ueyQ3XwBjKAoDEB5c80ps6dEDDoXzJBi+Pe1PS/hhVZq3rb5NqVzuJEBlPCA==",
|
||||
"python/iqpilot_private/context/reasoning_relay.cpython-312-aarch64-linux-gnu.so": "OD6pPW3nNwopVqsY0UXVyGrAREG7KNCEO59c5j6uIPuKHaSCu2z59tNG9WZ/mgfJCyhLCAN6K9BULmX2kwRTDA==",
|
||||
"python/iqpilot_private/context/speed_limit_input.cpython-312-aarch64-linux-gnu.so": "B4Pc4T9sPIuF9g35pB43YbaEzwX9h4IhPrvLzwf1HUmkPMT7knz2B1lgU0z8atz96F5OUpJSE2mG5usq5BzjAA==",
|
||||
"python/iqpilot_private/context/temporal_state.cpython-312-aarch64-linux-gnu.so": "KYtRL67X5HZ2vVmQfqW8XhnmDVK1vxpo2geVcZ7NXGZ3FFn5vcIweK2ZFVZoIgPfGvK5QMSONnLXPUNSfc9iCw=="
|
||||
"python/iqpilot_private/context/aux_inputs.cpython-312-aarch64-linux-gnu.so": "1CTTb0TqkzHb+66U+mwhtiB0xa5ETlJmr7Hzdl7HJjEAhZVp8LImLWJm785MsaSCkC1mr2wI81FTTCM+6KOtBg==",
|
||||
"python/iqpilot_private/context/cot_decode.cpython-312-aarch64-linux-gnu.so": "X1Tb8xcrygS+L0a/f0os516Q8mTPIQuCkVUNkChn77lDLc+B3UaHYiIeI7iyomzW3958NNlXo9lq+lRYw4TZBQ==",
|
||||
"python/iqpilot_private/context/drive_profile.cpython-312-aarch64-linux-gnu.so": "afh0sJAKVrv2p8wGD2USTgvpme2/DgFV+mb2YAchPNGtBDL+S+y5KaxKsKXUVHVN7wU36AKCj/6T777jIVz2Dg==",
|
||||
"python/iqpilot_private/context/feature_inputs.cpython-312-aarch64-linux-gnu.so": "ewMXJE1p5pz0uADnJROdarY8Cw85Uis5rAJvpotH+rVOoVoTaw/nO4MRbCeDj6Ko042QiCo7p/91cO5VT/OjDw==",
|
||||
"python/iqpilot_private/context/model_context.cpython-312-aarch64-linux-gnu.so": "4DeG7yQsLSC1kkOTusrCebb3yxQyTXZ4BbL4YDyIk9qKtGBPmPYj0x2J6b9HpAwchheqq9uNuko2nUkW3sMvCA==",
|
||||
"python/iqpilot_private/context/reasoning_relay.cpython-312-aarch64-linux-gnu.so": "1an09hj/ok9Fk4vted3GXczjV8v+qfUsjexRpKXnFeKdQNltv6oWosjjQvtEwzkikc43mtgIKAYoAxLBiS9lDw==",
|
||||
"python/iqpilot_private/context/speed_limit_input.cpython-312-aarch64-linux-gnu.so": "zyrasLvljreehe00C+GuDffQOE2OL3RDFy3dyCofB2WfB2EMcUyIWOFk+Ybtn+jJ9DQTHH063bDkURT5yviqDw==",
|
||||
"python/iqpilot_private/context/temporal_state.cpython-312-aarch64-linux-gnu.so": "hgeXamMIvzc3BLu9PtmlvI2jqi+x+g37TWvbrmfd78/z8Nzw68KoVIju3rKZcHU0BaXgnhc5sxlQV0wswk+3Cg=="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
NcjiBJcKitajD4QhhgJZWkc7JRypOIeWKlHvcc0gRMfLAHU7K3ogMd/ZYHUwWPaTZw0JS5zkUIPuZ7hN+chqDA==
|
||||
ODnEFb70mSlY+uG5Kl/aS6lYQ1fmNusxlf3O/ed2G/lPuA21fbzKmU+9XT9a83g9oAp5sLZuBV2zECPLewWiDg==
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
[
|
||||
"iqpilot.common.api",
|
||||
"iqpilot.common.api.base",
|
||||
"iqpilot.common.api.konn3kt",
|
||||
"iqpilot.common.basedir",
|
||||
"iqpilot.common.gpio",
|
||||
"iqpilot.common.i2c",
|
||||
@@ -8,13 +10,13 @@
|
||||
"iqpilot.common.swaglog",
|
||||
"iqpilot.common.time_helpers",
|
||||
"iqpilot.common.utils",
|
||||
"iqpilot.konn3kt.cloud_client",
|
||||
"iqpilot.selfdrive.iqmodeld.config",
|
||||
"iqpilot.selfdrive.iqmodeld.drive_profile",
|
||||
"iqpilot.selfdrive.iqmodeld.emac_model_meta",
|
||||
"iqpilot.selfdrive.iqmodeld.model_map_tile",
|
||||
"iqpilot.selfdrive.iqmodeld.models.fetcher",
|
||||
"iqpilot.selfdrive.iqmodeld.models.helpers",
|
||||
"iqpilot.selfdrive.iqmodeld.models.iqc",
|
||||
"iqpilot.selfdrive.iqmodeld.speed_limit_input",
|
||||
"iqpilot.selfdrive.iqmodeld.temporal_state",
|
||||
"iqpilot.system.hardware",
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import iqpilot.common.api.konn3kt
|
||||
|
||||
|
||||
class Api:
|
||||
def __init__(self, dongle_id):
|
||||
self.service = iqpilot.common.api.konn3kt.Konn3ktApi(dongle_id)
|
||||
|
||||
def request(self, method, endpoint, **params):
|
||||
return self.service.request(method, endpoint, **params)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return self.service.get(*args, **kwargs)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
return self.service.post(*args, **kwargs)
|
||||
|
||||
def get_token(self, payload_extra=None, expiry_hours=1):
|
||||
return self.service.get_token(payload_extra, expiry_hours)
|
||||
|
||||
|
||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
|
||||
return iqpilot.common.api.konn3kt.Konn3ktApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
|
||||
|
||||
|
||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||
return iqpilot.common.api.konn3kt.Konn3ktApi(None).get_key_pair()
|
||||
|
||||
@@ -7,10 +7,44 @@ from functools import lru_cache
|
||||
from iqpilot.system.hardware.hw import Paths
|
||||
from iqpilot.system.version import get_version
|
||||
|
||||
# name: jwt signature algorithm
|
||||
KEYS = {"id_rsa": "RS256",
|
||||
"id_ecdsa": "ES256"}
|
||||
|
||||
_CUSTODIAN_KEY = object()
|
||||
_IQKEEPER_SOCKET = "/run/iqkeeper/iqkeeper.sock"
|
||||
|
||||
|
||||
def _custodian_available() -> bool:
|
||||
import stat
|
||||
try:
|
||||
return stat.S_ISSOCK(os.stat(_IQKEEPER_SOCKET).st_mode)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def _custodian_sign_jwt(dongle_id: str, payload: dict, alg: str) -> str:
|
||||
import base64
|
||||
import json
|
||||
import socket
|
||||
nonce = base64.b64encode(os.urandom(16)).decode()
|
||||
packet = json.dumps({
|
||||
"v": 1,
|
||||
"op": "sign",
|
||||
"dongle_id": dongle_id,
|
||||
"payload": payload,
|
||||
"alg": alg,
|
||||
"nonce": nonce,
|
||||
}).encode()
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as sock:
|
||||
sock.settimeout(25.0)
|
||||
sock.connect(_IQKEEPER_SOCKET)
|
||||
sock.send(packet)
|
||||
reply_data = sock.recv(8192)
|
||||
reply = json.loads(reply_data) if reply_data else {}
|
||||
if reply.get("nonce") != nonce or not reply.get("ok"):
|
||||
raise PermissionError(f"custodian sign failed: {reply.get('error', 'unknown')}")
|
||||
return reply["token"]
|
||||
|
||||
|
||||
@lru_cache(maxsize=4)
|
||||
def load_signing_key(private_key: str):
|
||||
@@ -60,6 +94,9 @@ class BaseApi:
|
||||
}
|
||||
if payload_extra is not None:
|
||||
payload.update(payload_extra)
|
||||
if self.private_key is _CUSTODIAN_KEY:
|
||||
serializable = {k: v.isoformat() if isinstance(v, datetime) else v for k, v in payload.items()}
|
||||
return _custodian_sign_jwt(self.dongle_id, serializable, self.jwt_algorithm)
|
||||
key = load_signing_key(self.private_key) if self.private_key else self.private_key
|
||||
token = jwt.encode(payload, key, algorithm=self.jwt_algorithm)
|
||||
if isinstance(token, bytes):
|
||||
@@ -73,9 +110,25 @@ class BaseApi:
|
||||
return self.request(method, endpoint, timeout, access_token, session=session, json=json, **params)
|
||||
|
||||
@staticmethod
|
||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||
def get_key_pair() -> tuple[str, str | object, str] | tuple[None, None, None]:
|
||||
for key in KEYS:
|
||||
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
|
||||
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
|
||||
return KEYS[key], private.read(), public.read()
|
||||
priv_path = Paths.persist_root() + f'/comma/{key}'
|
||||
pub_path = priv_path + '.pub'
|
||||
if not os.path.isfile(priv_path) or not os.path.isfile(pub_path):
|
||||
continue
|
||||
try:
|
||||
with open(pub_path) as public:
|
||||
pub = public.read()
|
||||
except OSError:
|
||||
continue
|
||||
try:
|
||||
with open(priv_path) as private:
|
||||
return KEYS[key], private.read(), pub
|
||||
except PermissionError:
|
||||
import time
|
||||
for _attempt in range(10):
|
||||
if _custodian_available():
|
||||
return KEYS[key], _CUSTODIAN_KEY, pub
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
return None, None, None
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) 2026 IQ.Lvbs. All rights reserved.
|
||||
import os
|
||||
|
||||
from iqpilot.common.api.base import BaseApi
|
||||
@@ -5,7 +6,7 @@ from iqpilot.common.api.base import BaseApi
|
||||
API_HOST = os.getenv('API_HOST', 'https://api-iqlabs.konn3kt.com')
|
||||
|
||||
|
||||
class CommaConnectApi(BaseApi):
|
||||
class Konn3ktApi(BaseApi):
|
||||
def __init__(self, dongle_id):
|
||||
super().__init__(dongle_id, API_HOST)
|
||||
self.user_agent = "openpilot-"
|
||||
@@ -31,24 +31,44 @@ class Timer:
|
||||
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
|
||||
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
|
||||
|
||||
_sudo_available: bool | None = None
|
||||
|
||||
|
||||
def sudo_write(val: str, path: str) -> None:
|
||||
global _sudo_available
|
||||
try:
|
||||
with open(path, 'w') as f:
|
||||
f.write(str(val))
|
||||
except PermissionError:
|
||||
os.system(f"sudo chmod a+w {path}")
|
||||
try:
|
||||
return
|
||||
except OSError:
|
||||
pass
|
||||
# AGNOS 4.9.9.2 removes owner root; a privileged boot step opens these nodes to the stack so a direct write normally works. Only probe a non-interactive escalation once (interactive sudo blocks with no tty, and sudo -n itself can stall), and never let its failure or timeout propagate.
|
||||
if _sudo_available is False:
|
||||
return
|
||||
try:
|
||||
_sudo_available = subprocess.run(["sudo", "-n", "chmod", "a+w", path], timeout=2, capture_output=True).returncode == 0
|
||||
if _sudo_available:
|
||||
with open(path, 'w') as f:
|
||||
f.write(str(val))
|
||||
except PermissionError:
|
||||
# fallback for debugfs files
|
||||
os.system(f"sudo su -c 'echo {val} > {path}'")
|
||||
except Exception:
|
||||
_sudo_available = False
|
||||
|
||||
|
||||
def sudo_read(path: str) -> str:
|
||||
global _sudo_available
|
||||
try:
|
||||
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
|
||||
with open(path, encoding='utf8') as f:
|
||||
return f.read().strip()
|
||||
except OSError:
|
||||
pass
|
||||
if _sudo_available is False:
|
||||
return ""
|
||||
try:
|
||||
result = subprocess.run(["sudo", "-n", "cat", path], timeout=2, capture_output=True, text=True)
|
||||
_sudo_available = result.returncode == 0
|
||||
return result.stdout.strip()
|
||||
except Exception:
|
||||
_sudo_available = False
|
||||
return ""
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
"""
|
||||
Copyright ©️ IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
from iqpilot.common.api.base import BaseApi
|
||||
API_HOST = os.getenv('KONN3KT_API_HOST', 'https://api-iqlabs.konn3kt.com')
|
||||
|
||||
class Konn3ktApi(BaseApi):
|
||||
|
||||
def __init__(self, dongle_id):
|
||||
super().__init__(dongle_id, API_HOST)
|
||||
self.user_agent = "konn3kt-device-"
|
||||
|
||||
def get_token(self, expiry_hours=1):
|
||||
return super()._get_token(expiry_hours=expiry_hours)
|
||||
+33
@@ -305,7 +305,40 @@ def get_runtime_bundle_upgrade(bundle, params: Params = None, available_bundles=
|
||||
return _find_runtime_upgrade(bundle, params, available_bundles)
|
||||
|
||||
|
||||
_forced_bundle = None
|
||||
|
||||
|
||||
def load_default_model_session():
|
||||
"""Stage the shipped default model files and return a decoded default bundle
|
||||
WITHOUT persisting it as the active selection, so the next boot retries the
|
||||
user's own model instead of staying on the default forever."""
|
||||
bundle_dict = _load_default_bundle_dict()
|
||||
ensure_default_model_files(bundle_dict)
|
||||
return ModelBundle(**bundle_dict)
|
||||
|
||||
|
||||
class forced_active_bundle:
|
||||
"""Make get_active_bundle return `bundle` for the duration of the block, used to
|
||||
build a runner for a session-only fallback bundle without touching Params."""
|
||||
def __init__(self, bundle):
|
||||
self._bundle = bundle
|
||||
|
||||
def __enter__(self):
|
||||
global _forced_bundle
|
||||
self._prev = _forced_bundle
|
||||
_forced_bundle = self._bundle
|
||||
return self._bundle
|
||||
|
||||
def __exit__(self, *exc):
|
||||
global _forced_bundle
|
||||
_forced_bundle = self._prev
|
||||
return False
|
||||
|
||||
|
||||
def get_active_bundle(params: Params = None):
|
||||
if _forced_bundle is not None:
|
||||
return _forced_bundle
|
||||
|
||||
params = Params() if params is None else params
|
||||
|
||||
try:
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
|
||||
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
|
||||
|
||||
try:
|
||||
load_private_module(__name__, "iqpilot_private.models.iqc")
|
||||
except ProprietaryModuleMissing:
|
||||
from iqpilot.models_private_src.iqc import * # noqa: F403
|
||||
+21
-5
@@ -125,16 +125,32 @@ class Amplifier:
|
||||
print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}")
|
||||
|
||||
def set_configs(self, configs: list[AmpConfig]) -> bool:
|
||||
import threading
|
||||
|
||||
tries = 15
|
||||
backoff = 0.
|
||||
for i in range(tries):
|
||||
try:
|
||||
self._set_configs(configs)
|
||||
return True
|
||||
except OSError:
|
||||
result = [None]
|
||||
error = [None]
|
||||
def _worker():
|
||||
try:
|
||||
self._set_configs(configs)
|
||||
result[0] = True
|
||||
except OSError as e:
|
||||
error[0] = e
|
||||
t = threading.Thread(target=_worker, daemon=True)
|
||||
t.start()
|
||||
t.join(timeout=2)
|
||||
if t.is_alive():
|
||||
backoff += 0.1
|
||||
time.sleep(backoff)
|
||||
print(f"Failed to set amp config, {tries - i - 1} retries left")
|
||||
print(f"Failed to set amp config (timeout), {tries - i - 1} retries left")
|
||||
continue
|
||||
if result[0]:
|
||||
return True
|
||||
backoff += 0.1
|
||||
time.sleep(backoff)
|
||||
print(f"Failed to set amp config, {tries - i - 1} retries left")
|
||||
return False
|
||||
|
||||
def set_global_shutdown(self, amp_disabled: bool) -> bool:
|
||||
|
||||
+8
-6
@@ -469,8 +469,10 @@ class Tici(HardwareBase):
|
||||
if self.amplifier is not None:
|
||||
self.amplifier.initialize_configuration(self.get_device_type())
|
||||
|
||||
# Allow hardwared to write engagement status to kmsg
|
||||
os.system("sudo chmod a+w /dev/kmsg")
|
||||
try:
|
||||
subprocess.run(["sudo", "-n", "chmod", "a+w", "/dev/kmsg"], timeout=2, capture_output=True, check=False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL
|
||||
gpio_init(GPIO.SOM_ST_IO, True)
|
||||
@@ -514,10 +516,10 @@ class Tici(HardwareBase):
|
||||
affine_irq(3, "xhci-hcd:usb1")
|
||||
try:
|
||||
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
|
||||
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
|
||||
subprocess.call(["sudo", "taskset", "-pc", "3", pid], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except subprocess.CalledProcessException as e:
|
||||
print(str(e))
|
||||
subprocess.run(["sudo", "-n", "chrt", "-f", "-p", "1", pid], timeout=2, capture_output=True, check=False)
|
||||
subprocess.run(["sudo", "-n", "taskset", "-pc", "3", pid], timeout=2, capture_output=True, check=False)
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
def configure_modem(self):
|
||||
from iqpilot.common.params import Params
|
||||
|
||||
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"bundle_version": 1792551322,
|
||||
"python/_iqclosure/_vendored.json": {
|
||||
"mode": 420,
|
||||
"sha256": "c4e45105dedcba6f7a4abf84a6e012517ca82cd615165e162c2025ad59020fa1",
|
||||
"size": 3823
|
||||
"sha256": "00a3c1ea8d86878488abd7ee5ae5dce065c4b3d0fcf43072999303067807bc63",
|
||||
"size": 3668
|
||||
},
|
||||
"python/_iqclosure/iqpilot/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -16,18 +17,18 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "97d6e0d8cbd3e47797d734322b42d04efea4d95a2dae5a56f1baef3fd847aa0d",
|
||||
"size": 939
|
||||
"sha256": "1dd848ec6e867836a321a93f9ac218415c19528aa356a3e80969503285316b54",
|
||||
"size": 900
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/base.py": {
|
||||
"mode": 420,
|
||||
"sha256": "86b834e02e24a005c903f1ca059b1a572e37c17a87271726c90918cd04b6133b",
|
||||
"size": 3451
|
||||
"sha256": "5b312ec45e673d9337a89ea82a157152d1f64e5e48518807a592247787087609",
|
||||
"size": 4921
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/comma_connect.py": {
|
||||
"python/_iqclosure/iqpilot/common/api/konn3kt.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e7c2dd0b56fb8533c2cbc614e728a9ffc5ce7360524cfa62a02f5235bc23b9a8",
|
||||
"size": 267
|
||||
"sha256": "2282e19909079bfd77a089f480df5a229191c35f8523f005d20e2ea468a6b0bc",
|
||||
"size": 313
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/atlas_alerts.py": {
|
||||
"mode": 420,
|
||||
@@ -81,13 +82,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/realtime.py": {
|
||||
"mode": 420,
|
||||
"sha256": "016be524079ef32df03b2c135eab8f83da60a46fc7f143620b836c65f33e39a3",
|
||||
"size": 4423
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/spinner.py": {
|
||||
"mode": 420,
|
||||
"sha256": "33a2eb2aa3148492414035c5dc7150b3a69467905d7a3e58c8f65cfe70d3552c",
|
||||
"size": 1371
|
||||
"sha256": "0190cf1f239f8a2bda573524a72a4b6a74e7ed70aa4c1e5f72d1f1159d59cd60",
|
||||
"size": 4926
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/steer_delay.py": {
|
||||
"mode": 420,
|
||||
@@ -106,23 +102,18 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/utils.py": {
|
||||
"mode": 420,
|
||||
"sha256": "103fa6c24f8b963576c482a34d7ecb020168fd3e85156493ae4432ee3b7b2c06",
|
||||
"size": 5159
|
||||
"sha256": "c39b89a1ab34511dfa5f9efac783fde0e6d616963a416cf472bd1f604b57f84d",
|
||||
"size": 5892
|
||||
},
|
||||
"python/_iqclosure/iqpilot/konn3kt/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/konn3kt/cloud_client.py": {
|
||||
"mode": 420,
|
||||
"sha256": "4d58e62f8d628a8570881f2b87c82c801b46928fbb84f8dab8a81a9b55ba4e15",
|
||||
"size": 493
|
||||
},
|
||||
"python/_iqclosure/iqpilot/konn3kt/registration.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e9c37e8ae0a70c3a19ca1a33b290e94658b255fab153a941f50f8a29168d75c3",
|
||||
"size": 7816
|
||||
"sha256": "5a2b06d919adce513fb8a7e4e6f26d48e502e92dbd89f9af7a0e67e9114ab3be",
|
||||
"size": 2683
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -139,36 +130,16 @@
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/controls/lib/desire_helper.py": {
|
||||
"python/_iqclosure/iqpilot/selfdrive/controls/lib/action_engine.py": {
|
||||
"mode": 420,
|
||||
"sha256": "5d06908d9a5358d8b27e2a37b9f234f618966004aacb1761cf16c1a1acc74ab0",
|
||||
"size": 15285
|
||||
"sha256": "c3e33ae73bcdab89ba32b09e18712fe91018338907e8c81af056d447c5ef04c6",
|
||||
"size": 815
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/controls/lib/drive_helpers.py": {
|
||||
"mode": 420,
|
||||
"sha256": "94e202959eb80d841720ecd0581206fc4ac9d50ad2e2a6cf19f0de9914913988",
|
||||
"size": 3159
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/controls/lib/helpers/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/controls/lib/helpers/lane_change.py": {
|
||||
"mode": 420,
|
||||
"sha256": "0e4b4c54856d651d89e1c8896ebf8bb4f0a2efc0b8788a585094e06ecd6616d7",
|
||||
"size": 8461
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/controls/lib/helpers/lane_turn.py": {
|
||||
"mode": 420,
|
||||
"sha256": "d27bd9010967dd51dccc18ec005ff446ae965379b8e828ff7173331818723ec8",
|
||||
"size": 4667
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/controls/lib/helpers/lateral_edge_guard.py": {
|
||||
"mode": 420,
|
||||
"sha256": "6bb6bcb8e5323480bf1b992549279a7d4c17a1bd1bd260863cf7b61b887fa735",
|
||||
"size": 10227
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqlocd/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
@@ -189,6 +160,11 @@
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/aux_inputs.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e80699014729784ff72c64ac2cdf317a97e0f069ef99676581b1d179db5b4a0a",
|
||||
"size": 493
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/big_catalog.py": {
|
||||
"mode": 420,
|
||||
"sha256": "7ec5df43ce10b47ce925467e61100123e828b8e72bdab20bef4c8ac0ffd3cc7a",
|
||||
@@ -211,8 +187,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/daemon.py": {
|
||||
"mode": 420,
|
||||
"sha256": "62f51d922f6883bc094590a9d95c2c0849d858bbce30f35c2eba00ec9d3d9bad",
|
||||
"size": 36325
|
||||
"sha256": "2f94719d06907c4ac729b8aba063ad43079fde966813d55b6190af8d6c7da679",
|
||||
"size": 37375
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/drive_profile.py": {
|
||||
"mode": 420,
|
||||
@@ -221,13 +197,13 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/driving_action.py": {
|
||||
"mode": 420,
|
||||
"sha256": "46a46ea0675ce2f815021c63f3b1ab171c7a453c499a827f2a82b2f2e55d6f58",
|
||||
"size": 2484
|
||||
"sha256": "83c1155d90607a2ca119ce0797474e79d741aa0399ae86a22843ad580b5c8ef7",
|
||||
"size": 2644
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
|
||||
"mode": 420,
|
||||
"sha256": "a9f2bc8477b58a3df7196f4b40cdddf631e28051809e9bbfdd4c78148444c474",
|
||||
"size": 10891
|
||||
"sha256": "af83d52609ec4f2abe82f4e5f788ce4788bdc41cbf7b3de36dc63f1f598ef46c",
|
||||
"size": 12107
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
|
||||
"mode": 420,
|
||||
@@ -256,8 +232,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_bundle_downloader.py": {
|
||||
"mode": 420,
|
||||
"sha256": "83b0190f19e3f037c937a03b1988931729ed43d4dac92223aba0745697611ed8",
|
||||
"size": 12835
|
||||
"sha256": "62b13a05afbe9b5fb5d9e3d09c1491b37826ff840126c5e317a7ebf1edc28762",
|
||||
"size": 12060
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_channel.py": {
|
||||
"mode": 420,
|
||||
@@ -281,8 +257,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/combined_artifact.py": {
|
||||
"mode": 420,
|
||||
"sha256": "321e7223249dc7dba93effe8e834f225fb6067abf49da17fe9a6faeb71ba8e49",
|
||||
"size": 2786
|
||||
"sha256": "170639c92d69e4ae2493ca961fb95e75d42be9cedb2442521fe3ac7c25cb977c",
|
||||
"size": 2891
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/fetcher.py": {
|
||||
"mode": 420,
|
||||
@@ -291,14 +267,19 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/helpers.py": {
|
||||
"mode": 420,
|
||||
"sha256": "2fad977c3816ff824c126a92605f866f3bc0aa989ed8397e818bed481a41192f",
|
||||
"size": 14051
|
||||
"sha256": "9628f88e71f7fae2c3d62dd17618367b3c7d12749cfe2a4737410288888cd3bd",
|
||||
"size": 15008
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/inference_state.py": {
|
||||
"mode": 420,
|
||||
"sha256": "bdfd1c1b538e2342b17d08aa6ebe0c041c2f721025bcd7334434d1139fe6063d",
|
||||
"size": 268
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/iqc.py": {
|
||||
"mode": 420,
|
||||
"sha256": "54ec8e50513edae74ddb1c28015868183cb0c103de48386292d0b7fd83bcbb44",
|
||||
"size": 391
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
@@ -306,8 +287,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/model_runner.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e0eb9d7bff1da89d52b09596f7e7109fc25c3b9b86a89674382a7dde9e0a4199",
|
||||
"size": 8555
|
||||
"sha256": "c9645f9f377020a44d4a785de5930dcfc1b0028489461a94a3a1ddd10e7c7be3",
|
||||
"size": 8566
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -316,13 +297,13 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/combined_split_runner.py": {
|
||||
"mode": 420,
|
||||
"sha256": "621968a20fec17cc69d1d68735c317251dde33168e343f0ecb692aef310861c8",
|
||||
"size": 10894
|
||||
"sha256": "3d6cd89b8a45b246c0fea6dfc760d22cfdfc91557f95fff115bd186da7b76678",
|
||||
"size": 10897
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/fused_runner.py": {
|
||||
"mode": 420,
|
||||
"sha256": "25304fe7ae034d1bb8c9c86a2757704944539be6a0c69c322e75adc84f66e221",
|
||||
"size": 7885
|
||||
"sha256": "314023341e6fb0201c49975f1e3ed11ff074348e78bd42e893d2317e5c36ea14",
|
||||
"size": 7896
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/model_types.py": {
|
||||
"mode": 420,
|
||||
@@ -331,8 +312,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/supercombo_runner.py": {
|
||||
"mode": 420,
|
||||
"sha256": "120904be1407395a0d88d28fcba1fe426a2b84e381c3c2a0e6d9665868f492fe",
|
||||
"size": 10304
|
||||
"sha256": "9f4a0ab397ca4c10cf72318ee39072cb38258afe0a6d31950639fcad80387434",
|
||||
"size": 10315
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/supercombo_runtime.py": {
|
||||
"mode": 420,
|
||||
@@ -341,8 +322,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/runners/tinygrad/tinygrad_runner.py": {
|
||||
"mode": 420,
|
||||
"sha256": "58ffe0afb2cd9527d2a1d5bbebf0471af8c8210534e266ab80488a7f89bb06ba",
|
||||
"size": 7684
|
||||
"sha256": "175410433d287cfd3ac93e8fae19a5da409af61dab1d1a48731eb8aa7043c18c",
|
||||
"size": 7702
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/split_model_constants.py": {
|
||||
"mode": 420,
|
||||
@@ -516,13 +497,13 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/amplifier.py": {
|
||||
"mode": 420,
|
||||
"sha256": "98f46f8705259b00efae7274186b4e68201aad467a19de4f16d5f850d10ab0b5",
|
||||
"size": 7585
|
||||
"sha256": "38e521356c56ac85f35543b9457d36639e55ed2ac916f30ea3b86948e806541f",
|
||||
"size": 8016
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/hardware.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e8eae4e8a75e1751525471cf54a8555b7bfd7f08ad84bf2e75642b5745f222db",
|
||||
"size": 25601
|
||||
"sha256": "da5ecb280f7af14169903260dc941e3bfac033c93b184cb0abea0f1494c34582",
|
||||
"size": 25731
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/iwlist.py": {
|
||||
"mode": 420,
|
||||
@@ -575,49 +556,49 @@
|
||||
"size": 119
|
||||
},
|
||||
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "0dcf7516014011de4b56eee175e4e299fa5fee748f6e0b72169605e098c080bc",
|
||||
"size": 400040
|
||||
"mode": 416,
|
||||
"sha256": "fddf1c1e66a06c6815c7b3bf3b2d35f209bc245073db60542a76a56b28c28497",
|
||||
"size": 1327832
|
||||
},
|
||||
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "39263f739980829ebf32ba43abdf32dffa2c015944808ea64cb6ff1dc210a043",
|
||||
"size": 68152
|
||||
"mode": 416,
|
||||
"sha256": "8f552e5100f29394362210ced30a1e44a4b8227edb3f66fcf2b7dde2f3001e69",
|
||||
"size": 999816
|
||||
},
|
||||
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "6787635f4b79687dc54fd5e3c25b72285a5a8e04e81a28b5764deac6ce2492ed",
|
||||
"size": 333704
|
||||
"mode": 416,
|
||||
"sha256": "17dc02560735d0adf518df30d0fceb5432a1528807e44b2924d22bc4b9f61561",
|
||||
"size": 1393240
|
||||
},
|
||||
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "a3bab600ccec9e9edb5817d5a177cd6638a8a727a8d4b25c6e722d4ad038188b",
|
||||
"size": 675944
|
||||
"mode": 416,
|
||||
"sha256": "e99eed14d07496da9f4d085a03ae3380de93a58fadc9c8bf5247e6f07b0f57d2",
|
||||
"size": 1655512
|
||||
},
|
||||
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "9476c1b5934125917bc0a0dceca95274ebf47d45a1ed66122cb34ba050f40e34",
|
||||
"size": 597936
|
||||
"mode": 416,
|
||||
"sha256": "64904b6215365c7db4b2df51d1706f9483b411768bfaedd888a0e3a1701e0302",
|
||||
"size": 1655480
|
||||
},
|
||||
"python/iqpilot_private/emac/model_context.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "f47a9a61624b9cb5e71bfe979fbae403e04cee27db61f0ccf9b30effd1bad626",
|
||||
"size": 473440
|
||||
"mode": 416,
|
||||
"sha256": "80ae92f06aa2562acdb6d5c88be004bbc1bf731351640f926f42a30cbde43d39",
|
||||
"size": 1393208
|
||||
},
|
||||
"python/iqpilot_private/emac/native_launcher.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "b68e259978df7824d5e992653aa1861c6fbd746e97f67c753b5f3ca5c4a93f7b",
|
||||
"size": 268000
|
||||
"mode": 416,
|
||||
"sha256": "41fe239760108a0a2f1c3e96fb0dec4801bcbdad5b6ed5d4b92c10120715ed22",
|
||||
"size": 1196408
|
||||
},
|
||||
"python/iqpilot_private/emac/rh_images.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "ab3543fc0fe22a5778cb79d63b3ff48031a36ce6e873c79dbfe54d73277d7f16",
|
||||
"size": 206032
|
||||
"mode": 416,
|
||||
"sha256": "dbcb1f6103caecf536d6becfad9351381b0a9eef8742ea7df4c03c817c34e000",
|
||||
"size": 1262024
|
||||
},
|
||||
"python/iqpilot_private/emac/wire_codec.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "fe91bb1f156c49cecb4a4707a37affdab70b7d9125ff23e4081ca9494ba2c356",
|
||||
"size": 203152
|
||||
"mode": 416,
|
||||
"sha256": "22ef20fc1e70b73f135beb8abeb2831770cc39e0e2a44f3fc7bb44a5da811f93",
|
||||
"size": 1130760
|
||||
},
|
||||
"runtime": {
|
||||
"entries": {
|
||||
@@ -628,14 +609,14 @@
|
||||
}
|
||||
},
|
||||
"signatures": {
|
||||
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "nSEzB01BtGt3un5WVEbn1E5IaHnj7/zLt4lXIYOfBd/yqMBpuUCZ48SUhJU5hTFKMyiuyk3HG/72iwDWMCo1BA==",
|
||||
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "kUADWHWJJcjYKyumfG2b+XtgDgCQTMtz371MDjnngEv4zn0BnW3AlD+WfoggzBcd6FZ5DYNB8oyjea6BkQgMDA==",
|
||||
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "CbUzdCkb7mORLJaOMCeyLXEkeZzr3EYA1b5doki+q9AshoXNoSOkso+Uh7fApTNr1nqSy+QNfxBm8IMtNQt8Aw==",
|
||||
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "80cJCpENHXIyhxGAHRgqBEfyN7KBQ112YQMdmPe1LOiXASSTWTsu5QMUzzV3u+X5kk+1UqRC0NZ5PjoF/sFADg==",
|
||||
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "x4CkpFtIxtZpt3de1im0hi7jABTdJ3mB3OSN4apkeQALCdYdhFzbVasfSrtkpN6t+/CUEx3g5cLhslEUBt9lCw==",
|
||||
"python/iqpilot_private/emac/model_context.cpython-312-aarch64-linux-gnu.so": "ISg4GM8peP3ER4alhIe3+3ogJ39dNDq+prLO70AtEf/wqSfATPN09rt/kmIrveqgyTlGD2/v3uOvICnLotKlBQ==",
|
||||
"python/iqpilot_private/emac/native_launcher.cpython-312-aarch64-linux-gnu.so": "ttGirAmlJmDTRy2MseGmR/xSK9KkjXIe9eevQ+EJ2P2itPr1XKN8rOoFXaJdUSQaVlsXi/w/yAtLcNEBf0oABA==",
|
||||
"python/iqpilot_private/emac/rh_images.cpython-312-aarch64-linux-gnu.so": "/v2I9Y7/OT0GhFGw8MjKWsz4hk4R/l02V5VCxD5iIYZkGBKdy9UPFcBYWjg/zpfwF+cYqwp0SmlrZOBFmykbDw==",
|
||||
"python/iqpilot_private/emac/wire_codec.cpython-312-aarch64-linux-gnu.so": "jFsJk4sFSEtSamxV6f0dBS1kWYcFQ2KEPA+0UbDqfXf4I3+KSaJpr0CQTmfGhgIWYGxAnetunoEJMKsVrNPNDA=="
|
||||
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "c7oPgelyaOkjpN3BDwHkt1UZCGuXa97ocZ4CkNtOyeBxW+7R26hldG7nOaif+nuNmnYNE/ya7ucuPwPkWJXYDA==",
|
||||
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "6ypZpUAkpit8UhYLh0XqceQan9EduF4yQ4MA4GqmPvFUw7u/muSOaopK+lBaw1Xtg8yPbfR8DoGZu0uj3fsPDw==",
|
||||
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "LrhYCVk6U7yOKNcVJKEBQuOfxC6cJfeUn4IA8gpwmX+ZyaV/oUvv+nDYB37MZD72KRAarhLoTXgpAU9yG7AWCw==",
|
||||
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "zm5GDcrKM6wB3mr8bmmjF65QUCTjj8XtvLo1+NrP5wClZKAtVrc90Glfau8YApntZwK+uSKBeHRi2xqg4V14BQ==",
|
||||
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "HgA5ik+z5dVMtSJolJQ7ERbW53vlcbG9GoDKfLN+ANluWrd4q1ezQUDvJJ5JZERQEw8N8tho6du2HsPgY65mAQ==",
|
||||
"python/iqpilot_private/emac/model_context.cpython-312-aarch64-linux-gnu.so": "50yzbrZZAOWidHTH6nZ+2LMvJGJ2LV60GXN2yXdKfVERrd8BGZH8j/oOrG71lW3wf/htiZmZRLgIvEKXcUJSCw==",
|
||||
"python/iqpilot_private/emac/native_launcher.cpython-312-aarch64-linux-gnu.so": "suTF6e1oz3Lki7qhFQ5wdHa2m5ZccMmCVEy/BZ/6wqem2fxt8C3hd0OjXiM0Xyg7R+pumDpGOiL23uh2EZDcAw==",
|
||||
"python/iqpilot_private/emac/rh_images.cpython-312-aarch64-linux-gnu.so": "y0YKu5/ezBo04HGeD4mQVlBpRBJdHn4Y8mBt3HNo6f5mcWg9NhJ7T0u3hmvXl1JcMUnTeDDwNkVIi52VrK+5DA==",
|
||||
"python/iqpilot_private/emac/wire_codec.cpython-312-aarch64-linux-gnu.so": "P+fIzA2ScQzFeulAfgkx5favc2mzvlTybL+e59hBEwF+5bFE3V12DFUOEBga5Rbcv9VrvfG52dMIeysNmHHHCg=="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
gmKHJty4j+pkzT4cISfbob5EVVaY0wTii9yUBeuFC4ZF5ERST59xBav/C/5rCnBhk6ednbwTWq68l0uqgc4XDA==
|
||||
wRI3NynFc/uIF9F5+7fv4xjZh+64osm4CRaZhUM4JAnJXUaYYl5VDp+kZCDJw8cg/i/VKCRZMGNHq8+XE9eLAQ==
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
"iqpilot.common.api",
|
||||
"iqpilot.common.api.base",
|
||||
"iqpilot.common.api.comma_connect",
|
||||
"iqpilot.common.api.konn3kt",
|
||||
"iqpilot.common.atlas_alerts",
|
||||
"iqpilot.common.basedir",
|
||||
"iqpilot.common.constants",
|
||||
@@ -13,19 +13,15 @@
|
||||
"iqpilot.common.issue_debug",
|
||||
"iqpilot.common.logging_extra",
|
||||
"iqpilot.common.realtime",
|
||||
"iqpilot.common.spinner",
|
||||
"iqpilot.common.steer_delay",
|
||||
"iqpilot.common.swaglog",
|
||||
"iqpilot.common.time_helpers",
|
||||
"iqpilot.common.utils",
|
||||
"iqpilot.konn3kt.cloud_client",
|
||||
"iqpilot.konn3kt.registration",
|
||||
"iqpilot.selfdrive.controls.lib.desire_helper",
|
||||
"iqpilot.selfdrive.controls.lib.action_engine",
|
||||
"iqpilot.selfdrive.controls.lib.drive_helpers",
|
||||
"iqpilot.selfdrive.controls.lib.helpers.lane_change",
|
||||
"iqpilot.selfdrive.controls.lib.helpers.lane_turn",
|
||||
"iqpilot.selfdrive.controls.lib.helpers.lateral_edge_guard",
|
||||
"iqpilot.selfdrive.iqlocd.models.constants",
|
||||
"iqpilot.selfdrive.iqmodeld.aux_inputs",
|
||||
"iqpilot.selfdrive.iqmodeld.big_catalog",
|
||||
"iqpilot.selfdrive.iqmodeld.camera",
|
||||
"iqpilot.selfdrive.iqmodeld.config",
|
||||
@@ -47,6 +43,7 @@
|
||||
"iqpilot.selfdrive.iqmodeld.models.fetcher",
|
||||
"iqpilot.selfdrive.iqmodeld.models.helpers",
|
||||
"iqpilot.selfdrive.iqmodeld.models.inference_state",
|
||||
"iqpilot.selfdrive.iqmodeld.models.iqc",
|
||||
"iqpilot.selfdrive.iqmodeld.models.runners.model_runner",
|
||||
"iqpilot.selfdrive.iqmodeld.models.runners.tinygrad.combined_split_runner",
|
||||
"iqpilot.selfdrive.iqmodeld.models.runners.tinygrad.fused_runner",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import iqpilot.common.api.comma_connect
|
||||
import iqpilot.common.api.konn3kt
|
||||
|
||||
|
||||
class Api:
|
||||
def __init__(self, dongle_id):
|
||||
self.service = iqpilot.common.api.comma_connect.CommaConnectApi(dongle_id)
|
||||
self.service = iqpilot.common.api.konn3kt.Konn3ktApi(dongle_id)
|
||||
|
||||
def request(self, method, endpoint, **params):
|
||||
return self.service.request(method, endpoint, **params)
|
||||
@@ -19,8 +19,8 @@ class Api:
|
||||
|
||||
|
||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
|
||||
return iqpilot.common.api.comma_connect.CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
|
||||
return iqpilot.common.api.konn3kt.Konn3ktApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
|
||||
|
||||
|
||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||
return iqpilot.common.api.comma_connect.CommaConnectApi(None).get_key_pair()
|
||||
return iqpilot.common.api.konn3kt.Konn3ktApi(None).get_key_pair()
|
||||
|
||||
@@ -7,10 +7,44 @@ from functools import lru_cache
|
||||
from iqpilot.system.hardware.hw import Paths
|
||||
from iqpilot.system.version import get_version
|
||||
|
||||
# name: jwt signature algorithm
|
||||
KEYS = {"id_rsa": "RS256",
|
||||
"id_ecdsa": "ES256"}
|
||||
|
||||
_CUSTODIAN_KEY = object()
|
||||
_IQKEEPER_SOCKET = "/run/iqkeeper/iqkeeper.sock"
|
||||
|
||||
|
||||
def _custodian_available() -> bool:
|
||||
import stat
|
||||
try:
|
||||
return stat.S_ISSOCK(os.stat(_IQKEEPER_SOCKET).st_mode)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def _custodian_sign_jwt(dongle_id: str, payload: dict, alg: str) -> str:
|
||||
import base64
|
||||
import json
|
||||
import socket
|
||||
nonce = base64.b64encode(os.urandom(16)).decode()
|
||||
packet = json.dumps({
|
||||
"v": 1,
|
||||
"op": "sign",
|
||||
"dongle_id": dongle_id,
|
||||
"payload": payload,
|
||||
"alg": alg,
|
||||
"nonce": nonce,
|
||||
}).encode()
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as sock:
|
||||
sock.settimeout(25.0)
|
||||
sock.connect(_IQKEEPER_SOCKET)
|
||||
sock.send(packet)
|
||||
reply_data = sock.recv(8192)
|
||||
reply = json.loads(reply_data) if reply_data else {}
|
||||
if reply.get("nonce") != nonce or not reply.get("ok"):
|
||||
raise PermissionError(f"custodian sign failed: {reply.get('error', 'unknown')}")
|
||||
return reply["token"]
|
||||
|
||||
|
||||
@lru_cache(maxsize=4)
|
||||
def load_signing_key(private_key: str):
|
||||
@@ -60,6 +94,9 @@ class BaseApi:
|
||||
}
|
||||
if payload_extra is not None:
|
||||
payload.update(payload_extra)
|
||||
if self.private_key is _CUSTODIAN_KEY:
|
||||
serializable = {k: v.isoformat() if isinstance(v, datetime) else v for k, v in payload.items()}
|
||||
return _custodian_sign_jwt(self.dongle_id, serializable, self.jwt_algorithm)
|
||||
key = load_signing_key(self.private_key) if self.private_key else self.private_key
|
||||
token = jwt.encode(payload, key, algorithm=self.jwt_algorithm)
|
||||
if isinstance(token, bytes):
|
||||
@@ -73,9 +110,25 @@ class BaseApi:
|
||||
return self.request(method, endpoint, timeout, access_token, session=session, json=json, **params)
|
||||
|
||||
@staticmethod
|
||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||
def get_key_pair() -> tuple[str, str | object, str] | tuple[None, None, None]:
|
||||
for key in KEYS:
|
||||
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
|
||||
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
|
||||
return KEYS[key], private.read(), public.read()
|
||||
priv_path = Paths.persist_root() + f'/comma/{key}'
|
||||
pub_path = priv_path + '.pub'
|
||||
if not os.path.isfile(priv_path) or not os.path.isfile(pub_path):
|
||||
continue
|
||||
try:
|
||||
with open(pub_path) as public:
|
||||
pub = public.read()
|
||||
except OSError:
|
||||
continue
|
||||
try:
|
||||
with open(priv_path) as private:
|
||||
return KEYS[key], private.read(), pub
|
||||
except PermissionError:
|
||||
import time
|
||||
for _attempt in range(10):
|
||||
if _custodian_available():
|
||||
return KEYS[key], _CUSTODIAN_KEY, pub
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
return None, None, None
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) 2026 IQ.Lvbs. All rights reserved.
|
||||
import os
|
||||
|
||||
from iqpilot.common.api.base import BaseApi
|
||||
@@ -5,7 +6,7 @@ from iqpilot.common.api.base import BaseApi
|
||||
API_HOST = os.getenv('API_HOST', 'https://api-iqlabs.konn3kt.com')
|
||||
|
||||
|
||||
class CommaConnectApi(BaseApi):
|
||||
class Konn3ktApi(BaseApi):
|
||||
def __init__(self, dongle_id):
|
||||
super().__init__(dongle_id, API_HOST)
|
||||
self.user_agent = "openpilot-"
|
||||
@@ -62,10 +62,23 @@ def config_background_thread() -> None:
|
||||
set_core_affinity(list(range(os.cpu_count() or 1)))
|
||||
|
||||
|
||||
def _swap_active() -> bool:
|
||||
try:
|
||||
with open("/proc/swaps") as f:
|
||||
return len(f.read().splitlines()) > 1
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def lock_memory() -> None:
|
||||
"""mlockall this process so memory reclaim/compaction can't stall it. RT control
|
||||
procs only (locking ui/modeld would worsen pressure). Best-effort."""
|
||||
if sys.platform != 'linux' or PC:
|
||||
"""mlockall this process so swap can't evict its pages and stall the RT loop.
|
||||
RT control procs only (locking ui/modeld would worsen pressure). Best-effort.
|
||||
|
||||
No-op without active swap: with none, anonymous pages (thread stacks, heap)
|
||||
are already unreclaimable, so mlockall buys nothing — it only pins every 8 MB
|
||||
thread stack and MCL_FUTURE allocation in full (~400 MB/proc here) and, being
|
||||
non-migratable, starves CMA. The device runs swapless by design (iqos-zram)."""
|
||||
if sys.platform != 'linux' or PC or not _swap_active():
|
||||
return
|
||||
try:
|
||||
import ctypes
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
from iqpilot.common.basedir import BASEDIR
|
||||
|
||||
|
||||
class Spinner:
|
||||
def __init__(self):
|
||||
try:
|
||||
self.spinner_proc = subprocess.Popen(["./spinner.py"],
|
||||
stdin=subprocess.PIPE,
|
||||
cwd=os.path.join(BASEDIR, "iqpilot", "system", "ui"),
|
||||
close_fds=True)
|
||||
except OSError:
|
||||
self.spinner_proc = None
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def update(self, spinner_text: str):
|
||||
if self.spinner_proc is not None:
|
||||
self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n")
|
||||
try:
|
||||
self.spinner_proc.stdin.flush()
|
||||
except BrokenPipeError:
|
||||
pass
|
||||
|
||||
def update_progress(self, cur: float, total: float):
|
||||
self.update(str(round(100 * cur / total)))
|
||||
|
||||
def close(self):
|
||||
if self.spinner_proc is not None:
|
||||
self.spinner_proc.kill()
|
||||
try:
|
||||
self.spinner_proc.communicate(timeout=2.)
|
||||
except subprocess.TimeoutExpired:
|
||||
print("WARNING: failed to kill spinner")
|
||||
self.spinner_proc = None
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import time
|
||||
with Spinner() as s:
|
||||
s.update("Spinner text")
|
||||
time.sleep(5.0)
|
||||
print("gone")
|
||||
time.sleep(5.0)
|
||||
@@ -31,24 +31,44 @@ class Timer:
|
||||
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
|
||||
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
|
||||
|
||||
_sudo_available: bool | None = None
|
||||
|
||||
|
||||
def sudo_write(val: str, path: str) -> None:
|
||||
global _sudo_available
|
||||
try:
|
||||
with open(path, 'w') as f:
|
||||
f.write(str(val))
|
||||
except PermissionError:
|
||||
os.system(f"sudo chmod a+w {path}")
|
||||
try:
|
||||
return
|
||||
except OSError:
|
||||
pass
|
||||
# AGNOS 4.9.9.2 removes owner root; a privileged boot step opens these nodes to the stack so a direct write normally works. Only probe a non-interactive escalation once (interactive sudo blocks with no tty, and sudo -n itself can stall), and never let its failure or timeout propagate.
|
||||
if _sudo_available is False:
|
||||
return
|
||||
try:
|
||||
_sudo_available = subprocess.run(["sudo", "-n", "chmod", "a+w", path], timeout=2, capture_output=True).returncode == 0
|
||||
if _sudo_available:
|
||||
with open(path, 'w') as f:
|
||||
f.write(str(val))
|
||||
except PermissionError:
|
||||
# fallback for debugfs files
|
||||
os.system(f"sudo su -c 'echo {val} > {path}'")
|
||||
except Exception:
|
||||
_sudo_available = False
|
||||
|
||||
|
||||
def sudo_read(path: str) -> str:
|
||||
global _sudo_available
|
||||
try:
|
||||
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
|
||||
with open(path, encoding='utf8') as f:
|
||||
return f.read().strip()
|
||||
except OSError:
|
||||
pass
|
||||
if _sudo_available is False:
|
||||
return ""
|
||||
try:
|
||||
result = subprocess.run(["sudo", "-n", "cat", path], timeout=2, capture_output=True, text=True)
|
||||
_sudo_available = result.returncode == 0
|
||||
return result.stdout.strip()
|
||||
except Exception:
|
||||
_sudo_available = False
|
||||
return ""
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
"""
|
||||
Copyright ©️ IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
from iqpilot.common.api.base import BaseApi
|
||||
API_HOST = os.getenv('KONN3KT_API_HOST', 'https://api-iqlabs.konn3kt.com')
|
||||
|
||||
class Konn3ktApi(BaseApi):
|
||||
|
||||
def __init__(self, dongle_id):
|
||||
super().__init__(dongle_id, API_HOST)
|
||||
self.user_agent = "konn3kt-device-"
|
||||
|
||||
def get_token(self, expiry_hours=1):
|
||||
return super()._get_token(expiry_hours=expiry_hours)
|
||||
@@ -1,18 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import jwt
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
import re
|
||||
import secrets
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
|
||||
from datetime import datetime, timedelta, UTC
|
||||
from iqpilot.common.api import api_get, get_key_pair
|
||||
from iqpilot.common.params import Params
|
||||
from iqpilot.common.spinner import Spinner
|
||||
from iqpilot.system.hardware import HARDWARE
|
||||
from iqpilot.system.hardware.hw import Paths
|
||||
from iqpilot.common.swaglog import cloudlog
|
||||
|
||||
@@ -35,6 +29,10 @@ def _read_persist_dongle_id() -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def is_valid_dongle_id(dongle_id: str | None) -> bool:
|
||||
return bool(dongle_id and _DONGLE_ID_RE.fullmatch(dongle_id))
|
||||
|
||||
|
||||
def get_cached_dongle_id(params: Params | None = None, prefer_readonly: bool = True) -> str | None:
|
||||
ro = _read_persist_dongle_id()
|
||||
if is_valid_dongle_id(ro):
|
||||
@@ -46,8 +44,8 @@ def get_cached_dongle_id(params: Params | None = None, prefer_readonly: bool = T
|
||||
if v and v != UNREGISTERED_DONGLE_ID:
|
||||
return v.lower() if is_valid_dongle_id(v) else v
|
||||
return ro or None
|
||||
def is_valid_dongle_id(dongle_id: str | None) -> bool:
|
||||
return bool(dongle_id and _DONGLE_ID_RE.fullmatch(dongle_id))
|
||||
|
||||
|
||||
def get_or_create_dongle_id(params: Params | None = None, prefer_readonly: bool = True) -> str:
|
||||
p = Params() if params is None else params
|
||||
dongle_id = get_cached_dongle_id(p, prefer_readonly=prefer_readonly)
|
||||
@@ -57,164 +55,32 @@ def get_or_create_dongle_id(params: Params | None = None, prefer_readonly: bool
|
||||
p.put("DongleId", dongle_id)
|
||||
cloudlog.warning(f"generated new DongleId={dongle_id} (no readonly dongle_id found)")
|
||||
return dongle_id
|
||||
|
||||
|
||||
_impl = None
|
||||
|
||||
|
||||
def _registration_impl():
|
||||
global _impl
|
||||
if _impl is None:
|
||||
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
|
||||
try:
|
||||
_impl = load_private_module(__name__, "iqpilot_private.konn3kt.registration.reg_core")
|
||||
except ProprietaryModuleMissing:
|
||||
from konn3kt_private.registration import reg_core as _impl
|
||||
return _impl
|
||||
|
||||
|
||||
def ensure_dev_pairing_identity(params: Params | None = None, force_reset: bool = False) -> dict[str, str]:
|
||||
p = Params() if params is None else params
|
||||
|
||||
persist_dir = Path(Paths.persist_root()) / "comma"
|
||||
persist_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
dongle_path = persist_dir / "dongle_id"
|
||||
priv_path = persist_dir / "id_rsa"
|
||||
pub_path = persist_dir / "id_rsa.pub"
|
||||
|
||||
if force_reset:
|
||||
for fp in (dongle_path, priv_path, pub_path):
|
||||
try:
|
||||
fp.unlink(missing_ok=True)
|
||||
except Exception:
|
||||
cloudlog.exception(f"failed to remove {fp}")
|
||||
try:
|
||||
(persist_dir / "konn3kt_prime_type").unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
p.remove("PrimeType")
|
||||
except Exception:
|
||||
pass
|
||||
forced_dongle = os.getenv("KONN3KT_DEV_DONGLE_ID")
|
||||
dongle_id = forced_dongle.strip().lower() if forced_dongle else None
|
||||
if dongle_id and not is_valid_dongle_id(dongle_id):
|
||||
cloudlog.error("KONN3KT_DEV_DONGLE_ID must be 16 hex chars")
|
||||
dongle_id = None
|
||||
if dongle_id is None:
|
||||
existing = None
|
||||
try:
|
||||
existing = dongle_path.read_text().strip().lower() if dongle_path.is_file() else None
|
||||
except Exception:
|
||||
cloudlog.exception("failed reading existing dev dongle_id")
|
||||
dongle_id = existing if is_valid_dongle_id(existing) else secrets.token_hex(8)
|
||||
try:
|
||||
dongle_path.write_text(dongle_id)
|
||||
except Exception:
|
||||
cloudlog.exception("failed writing dev dongle_id")
|
||||
p.put("DongleId", dongle_id)
|
||||
p.put("HardwareSerial", p.get("HardwareSerial") or f"DEV-{dongle_id}")
|
||||
if force_reset or (not priv_path.is_file()) or (not pub_path.is_file()):
|
||||
try:
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||
priv_bytes = key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption(),
|
||||
)
|
||||
pub_bytes = key.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
priv_path.write_bytes(priv_bytes)
|
||||
pub_path.write_bytes(pub_bytes)
|
||||
except Exception:
|
||||
cloudlog.exception("failed generating dev RSA keys")
|
||||
raise
|
||||
return {
|
||||
"dongle_id": dongle_id,
|
||||
"serial": p.get("HardwareSerial") or f"DEV-{dongle_id}",
|
||||
"persist_dir": str(persist_dir),
|
||||
}
|
||||
def _normalize_imei(value: str | None) -> str:
|
||||
return value or ""
|
||||
return _registration_impl().ensure_dev_pairing_identity(params, force_reset)
|
||||
|
||||
|
||||
def get_registration_identifiers(wait_timeout: float = IMEI_WAIT_TIMEOUT, show_spinner: bool = False) -> tuple[str, str, str]:
|
||||
serial = HARDWARE.get_serial()
|
||||
spinner = Spinner() if show_spinner else None
|
||||
start_time = time.monotonic()
|
||||
imei1: str | None = None
|
||||
imei2: str | None = None
|
||||
|
||||
while time.monotonic() - start_time < wait_timeout:
|
||||
try:
|
||||
imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
|
||||
if imei1 or imei2:
|
||||
break
|
||||
except RuntimeError as e:
|
||||
if "no modems" in str(e).lower():
|
||||
cloudlog.warning("No cellular modem available, proceeding without IMEI")
|
||||
break
|
||||
cloudlog.exception("Error getting imei, trying again...")
|
||||
except Exception:
|
||||
cloudlog.exception("Error getting imei, trying again...")
|
||||
time.sleep(1)
|
||||
|
||||
imei1 = _normalize_imei(imei1)
|
||||
imei2 = _normalize_imei(imei2)
|
||||
|
||||
if not imei1 and not imei2:
|
||||
cloudlog.warning(f"proceeding with serial-only registration for serial={serial}")
|
||||
if spinner is not None:
|
||||
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1 or None}, {imei2 or None})")
|
||||
spinner.close()
|
||||
|
||||
return serial, imei1, imei2
|
||||
return _registration_impl().get_registration_identifiers(wait_timeout, show_spinner)
|
||||
|
||||
|
||||
def register(show_spinner=False) -> str | None:
|
||||
params = Params()
|
||||
|
||||
dongle_id: str | None = get_cached_dongle_id(params, prefer_readonly=True)
|
||||
if dongle_id in ("", UNREGISTERED_DONGLE_ID):
|
||||
dongle_id = None
|
||||
|
||||
jwt_algo, private_key, public_key = get_key_pair()
|
||||
|
||||
if not public_key:
|
||||
dongle_id = UNREGISTERED_DONGLE_ID
|
||||
cloudlog.warning("missing public key")
|
||||
elif dongle_id is None:
|
||||
if show_spinner:
|
||||
spinner = Spinner()
|
||||
spinner.update("registering device")
|
||||
|
||||
serial, imei1, imei2 = get_registration_identifiers(wait_timeout=IMEI_WAIT_TIMEOUT, show_spinner=False)
|
||||
|
||||
backoff = 0
|
||||
start_time = time.monotonic()
|
||||
while True:
|
||||
try:
|
||||
register_token = jwt.encode({'register': True, 'exp': datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1)},
|
||||
cast(str, private_key), algorithm=jwt_algo)
|
||||
cloudlog.info("getting pilotauth")
|
||||
cloudlog.info("getting pilotauth")
|
||||
resp = api_get("v2/pilotauth/", method='POST', timeout=15,
|
||||
imei=imei1, imei2=imei2, serial=serial, public_key=public_key, register_token=register_token)
|
||||
|
||||
if resp.status_code in (400, 402, 403):
|
||||
cloudlog.info(f"Unable to register device, got {resp.status_code}")
|
||||
dongle_id = UNREGISTERED_DONGLE_ID
|
||||
else:
|
||||
dongleauth = json.loads(resp.text)
|
||||
dongle_id = dongleauth["dongle_id"]
|
||||
break
|
||||
except Exception:
|
||||
cloudlog.exception("failed to authenticate")
|
||||
backoff = min(backoff + 1, 15)
|
||||
time.sleep(backoff)
|
||||
|
||||
if time.monotonic() - start_time > 60 and show_spinner:
|
||||
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})")
|
||||
return UNREGISTERED_DONGLE_ID
|
||||
|
||||
if show_spinner:
|
||||
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1 or None}, {imei2 or None})")
|
||||
spinner.close()
|
||||
|
||||
if dongle_id:
|
||||
params.put("DongleId", dongle_id)
|
||||
from iqpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
|
||||
set_offroad_alert("Offroad_UnregisteredHardware", False)
|
||||
return dongle_id
|
||||
return _registration_impl().register(show_spinner)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
|
||||
|
||||
try:
|
||||
load_private_module(__name__, "iqpilot_private.navd.action_engine")
|
||||
except ProprietaryModuleMissing:
|
||||
from iqpilot.cereal import custom, log
|
||||
|
||||
class ActionEngine:
|
||||
def __init__(self):
|
||||
self.desire = log.Desire.none
|
||||
self.lane_change_state = log.LaneChangeState.off
|
||||
self.lane_change_direction = log.LaneChangeDirection.none
|
||||
self.lane_turn_direction = custom.IQTurnSignalDirection.none
|
||||
self.lateral_edge_block = custom.IQLateralEdgeBlock.none
|
||||
|
||||
def update(self, carstate, lateral_active, lane_change_prob, nav_state=None, modeldata=None, radar_state=None):
|
||||
return None
|
||||
-360
@@ -1,360 +0,0 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
|
||||
from iqpilot.cereal import car, custom, log
|
||||
|
||||
from iqpilot.common.constants import CV
|
||||
from iqpilot.common.params import Params
|
||||
from iqpilot.common.realtime import DT_MDL
|
||||
from iqpilot.selfdrive.controls.lib.helpers.lane_change import (
|
||||
IQLaneSwapController,
|
||||
AutoLaneChangeMode,
|
||||
NavExitLaneChangeController,
|
||||
)
|
||||
from iqpilot.selfdrive.controls.lib.helpers.lateral_edge_guard import LateralEdgeGuard
|
||||
from iqpilot.selfdrive.controls.lib.helpers.lane_turn import IQNavTurnController
|
||||
|
||||
LaneChangeState = log.LaneChangeState
|
||||
LaneChangeDirection = log.LaneChangeDirection
|
||||
TurnDirection = custom.IQTurnSignalDirection
|
||||
LateralEdgeBlock = custom.IQLateralEdgeBlock
|
||||
NavManeuverPhase = custom.IQNavState.ManeuverPhase
|
||||
|
||||
LANE_CHANGE_SPEED_MIN = 20 * CV.MPH_TO_MS
|
||||
LANE_CHANGE_TIME_MAX = 10.0
|
||||
LANE_CHANGE_CANCEL_TORQUE_FRAMES = round(0.25 / DT_MDL)
|
||||
# only the rising edge reaches the model and it stays visible for the 5 s the history spans, so one
|
||||
# edge per 5 s keeps the turn alive through the maneuver without stacking edges that cut the corner
|
||||
TURN_DESIRE_PULSE_CYCLE_FRAMES = round(5.0 / DT_MDL)
|
||||
TURN_DESIRE_PULSE_HOLD_FRAMES = TURN_DESIRE_PULSE_CYCLE_FRAMES - 1
|
||||
TURN_RECOIL_HEADING_RAD = math.radians(55.0)
|
||||
TURN_DESIRE_MAX_SECONDS = 25.0
|
||||
TURN_DESIRE_MOVING_SPEED = 0.5
|
||||
|
||||
_LANE_CHANGE_DESIRES = {
|
||||
(LaneChangeDirection.none, LaneChangeState.off): log.Desire.none,
|
||||
(LaneChangeDirection.none, LaneChangeState.preLaneChange): log.Desire.none,
|
||||
(LaneChangeDirection.none, LaneChangeState.laneChangeStarting): log.Desire.none,
|
||||
(LaneChangeDirection.none, LaneChangeState.laneChangeFinishing): log.Desire.none,
|
||||
(LaneChangeDirection.left, LaneChangeState.off): log.Desire.none,
|
||||
(LaneChangeDirection.left, LaneChangeState.preLaneChange): log.Desire.none,
|
||||
(LaneChangeDirection.left, LaneChangeState.laneChangeStarting): log.Desire.laneChangeLeft,
|
||||
(LaneChangeDirection.left, LaneChangeState.laneChangeFinishing): log.Desire.laneChangeLeft,
|
||||
(LaneChangeDirection.right, LaneChangeState.off): log.Desire.none,
|
||||
(LaneChangeDirection.right, LaneChangeState.preLaneChange): log.Desire.none,
|
||||
(LaneChangeDirection.right, LaneChangeState.laneChangeStarting): log.Desire.laneChangeRight,
|
||||
(LaneChangeDirection.right, LaneChangeState.laneChangeFinishing): log.Desire.laneChangeRight,
|
||||
}
|
||||
|
||||
_TURN_DESIRES = {
|
||||
TurnDirection.none: log.Desire.none,
|
||||
TurnDirection.turnLeft: log.Desire.turnLeft,
|
||||
TurnDirection.turnRight: log.Desire.turnRight,
|
||||
}
|
||||
|
||||
_PULSED_TURN_DESIRES = {
|
||||
log.Desire.turnLeft,
|
||||
log.Desire.turnRight,
|
||||
}
|
||||
|
||||
|
||||
def turn_desire(turn_direction) -> log.Desire:
|
||||
return _TURN_DESIRES[getattr(turn_direction, "raw", turn_direction)]
|
||||
|
||||
|
||||
def _direction_from_blinkers(carstate) -> int:
|
||||
if carstate.leftBlinker:
|
||||
return LaneChangeDirection.left
|
||||
if carstate.rightBlinker:
|
||||
return LaneChangeDirection.right
|
||||
return LaneChangeDirection.none
|
||||
|
||||
|
||||
def _steering_nudge_matches(carstate, direction: int) -> bool:
|
||||
if not carstate.steeringPressed:
|
||||
return False
|
||||
return (
|
||||
(direction == LaneChangeDirection.left and carstate.steeringTorque > 0) or
|
||||
(direction == LaneChangeDirection.right and carstate.steeringTorque < 0)
|
||||
)
|
||||
|
||||
|
||||
def _opposite_direction(direction: int) -> int:
|
||||
if direction == LaneChangeDirection.left:
|
||||
return LaneChangeDirection.right
|
||||
if direction == LaneChangeDirection.right:
|
||||
return LaneChangeDirection.left
|
||||
return LaneChangeDirection.none
|
||||
|
||||
|
||||
def _blinker_matches(carstate, direction: int) -> bool:
|
||||
return (
|
||||
(direction == LaneChangeDirection.left and carstate.leftBlinker and not carstate.rightBlinker) or
|
||||
(direction == LaneChangeDirection.right and carstate.rightBlinker and not carstate.leftBlinker)
|
||||
)
|
||||
|
||||
|
||||
def _blindspot_matches(carstate, direction: int) -> bool:
|
||||
return (
|
||||
(direction == LaneChangeDirection.left and carstate.leftBlindspot) or
|
||||
(direction == LaneChangeDirection.right and carstate.rightBlindspot)
|
||||
)
|
||||
|
||||
|
||||
def _read_enable_bsm() -> bool:
|
||||
try:
|
||||
with car.CarParams.from_bytes(Params().get("CarParams")) as cp:
|
||||
return bool(cp.enableBsm)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
class DesireHelper:
|
||||
def __init__(self):
|
||||
self.lane_change_state = LaneChangeState.off
|
||||
self.lane_change_direction = LaneChangeDirection.none
|
||||
self.lane_change_timer = 0.0
|
||||
self.lane_change_ll_prob = 1.0
|
||||
self.lane_change_counter_torque_frames = 0
|
||||
self.prev_one_blinker = False
|
||||
self.prev_nav_exit_active = False
|
||||
self.nav_exit_rearmed = False
|
||||
self.desire = log.Desire.none
|
||||
|
||||
self.alc = IQLaneSwapController(self)
|
||||
self.lane_turn_controller = IQNavTurnController(self)
|
||||
self.nav_exit = NavExitLaneChangeController(_read_enable_bsm())
|
||||
self.lateral_edge_guard = LateralEdgeGuard()
|
||||
self.lateral_edge_block = LateralEdgeBlock.none
|
||||
self.lane_turn_direction = TurnDirection.none
|
||||
self.nav_turn_direction = TurnDirection.none
|
||||
self.turn_desire_frames = 0
|
||||
self.turn_desire_cycle_input = log.Desire.none
|
||||
self.turn_heading_rad = 0.0
|
||||
self.turn_desire_seconds = 0.0
|
||||
self.turn_recoiled = False
|
||||
|
||||
@staticmethod
|
||||
def get_lane_change_direction(carstate):
|
||||
return _direction_from_blinkers(carstate)
|
||||
|
||||
@staticmethod
|
||||
def _nav_turn_desire(nav_state):
|
||||
if nav_state is None or not getattr(nav_state, "active", False):
|
||||
return TurnDirection.none
|
||||
if getattr(nav_state, "maneuverPhase", NavManeuverPhase.none) != NavManeuverPhase.turnActive:
|
||||
return TurnDirection.none
|
||||
if not getattr(nav_state, "shouldSendTurnDesire", False):
|
||||
return TurnDirection.none
|
||||
return getattr(nav_state, "turnDesireDirection", TurnDirection.none)
|
||||
|
||||
def _clear_lane_change(self) -> None:
|
||||
self.lane_change_state = LaneChangeState.off
|
||||
self.lane_change_direction = LaneChangeDirection.none
|
||||
|
||||
def _refresh_turn_overrides(self, carstate, nav_state) -> bool:
|
||||
speed_mps = carstate.vEgo
|
||||
self.lane_turn_controller.update_params()
|
||||
self.lane_turn_controller.update_lane_turn(
|
||||
blindspot_left=carstate.leftBlindspot,
|
||||
blindspot_right=carstate.rightBlindspot,
|
||||
left_blinker=carstate.leftBlinker,
|
||||
right_blinker=carstate.rightBlinker,
|
||||
v_ego=speed_mps,
|
||||
)
|
||||
self.lane_turn_direction = self.lane_turn_controller.get_turn_direction()
|
||||
self.nav_turn_direction = self._nav_turn_desire(nav_state)
|
||||
|
||||
self.nav_exit.update_params()
|
||||
self.nav_exit.update(nav_state, carstate)
|
||||
return bool(self.nav_exit.active)
|
||||
|
||||
def _reset_required(self, lateral_active: bool, nav_exit_active: bool) -> bool:
|
||||
timed_out = self.lane_change_timer > LANE_CHANGE_TIME_MAX
|
||||
feature_disabled = self.alc.lane_change_set_timer == AutoLaneChangeMode.OFF and not nav_exit_active
|
||||
return (not lateral_active) or timed_out or feature_disabled
|
||||
|
||||
def _begin_from_idle(self, one_blinker: bool, nav_exit_active: bool, below_speed: bool) -> None:
|
||||
if below_speed:
|
||||
return
|
||||
if one_blinker and not self.prev_one_blinker:
|
||||
self.lane_change_state = LaneChangeState.preLaneChange
|
||||
self.lane_change_direction = _direction_from_blinkers(self._last_carstate)
|
||||
self.lane_change_ll_prob = 1.0
|
||||
return
|
||||
if nav_exit_active and not self.prev_nav_exit_active:
|
||||
self.lane_change_state = LaneChangeState.preLaneChange
|
||||
self.lane_change_direction = self.nav_exit.direction
|
||||
self.lane_change_ll_prob = 1.0
|
||||
|
||||
def _refresh_requested_direction(self, one_blinker: bool, nav_exit_active: bool) -> None:
|
||||
if one_blinker:
|
||||
self.lane_change_direction = _direction_from_blinkers(self._last_carstate)
|
||||
elif nav_exit_active:
|
||||
self.lane_change_direction = self.nav_exit.direction
|
||||
|
||||
def _step_pre_lane_change(self, one_blinker: bool, nav_exit_active: bool, below_speed: bool) -> None:
|
||||
self._refresh_requested_direction(one_blinker, nav_exit_active)
|
||||
blindspot_detected = _blindspot_matches(self._last_carstate, self.lane_change_direction)
|
||||
self.lateral_edge_block = self.lateral_edge_guard.block_for_direction(self.lane_change_direction)
|
||||
lateral_edge_blocked = self.lateral_edge_block != LateralEdgeBlock.none
|
||||
steering_ready = _steering_nudge_matches(self._last_carstate, self.lane_change_direction)
|
||||
# an exit can need more than one lane, but only the first move may start without a driver nudge
|
||||
nav_auto_start = nav_exit_active and self.nav_exit.auto_allowed and not self.nav_exit_rearmed
|
||||
|
||||
self.alc.update_lane_change(blindspot_detected=blindspot_detected, brake_pressed=self._last_carstate.brakePressed)
|
||||
allowed_to_launch = steering_ready or self.alc.auto_lane_change_allowed or nav_auto_start
|
||||
|
||||
if (not (one_blinker or nav_exit_active)) or below_speed:
|
||||
self._clear_lane_change()
|
||||
elif allowed_to_launch and not blindspot_detected and not lateral_edge_blocked:
|
||||
self.lane_change_state = LaneChangeState.laneChangeStarting
|
||||
|
||||
def _step_lane_change_starting(self, lane_change_prob: float) -> None:
|
||||
self.lane_change_ll_prob = max(self.lane_change_ll_prob - (2.0 * DT_MDL), 0.0)
|
||||
if lane_change_prob < 0.02 and self.lane_change_ll_prob < 0.01:
|
||||
self.lane_change_state = LaneChangeState.laneChangeFinishing
|
||||
|
||||
def _step_lane_change_finishing(self, one_blinker: bool, nav_exit_active: bool) -> None:
|
||||
self.lane_change_ll_prob = min(self.lane_change_ll_prob + DT_MDL, 1.0)
|
||||
if self.lane_change_ll_prob <= 0.99:
|
||||
return
|
||||
self.lane_change_direction = LaneChangeDirection.none
|
||||
if one_blinker:
|
||||
self.lane_change_state = LaneChangeState.preLaneChange
|
||||
elif nav_exit_active:
|
||||
self.lane_change_state = LaneChangeState.preLaneChange
|
||||
self.lane_change_direction = self.nav_exit.direction
|
||||
self.nav_exit_rearmed = True
|
||||
else:
|
||||
self.lane_change_state = LaneChangeState.off
|
||||
|
||||
def _driver_cancels_lane_change(self) -> bool:
|
||||
if self.lane_change_state not in (LaneChangeState.laneChangeStarting, LaneChangeState.laneChangeFinishing):
|
||||
self.lane_change_counter_torque_frames = 0
|
||||
return False
|
||||
opposite = _opposite_direction(self.lane_change_direction)
|
||||
if _steering_nudge_matches(self._last_carstate, opposite):
|
||||
self.lane_change_counter_torque_frames += 1
|
||||
else:
|
||||
self.lane_change_counter_torque_frames = 0
|
||||
return (_blinker_matches(self._last_carstate, opposite) or
|
||||
self.lane_change_counter_torque_frames >= LANE_CHANGE_CANCEL_TORQUE_FRAMES)
|
||||
|
||||
def _cancel_lane_change(self) -> None:
|
||||
self._clear_lane_change()
|
||||
self.lane_change_ll_prob = 1.0
|
||||
self.lane_change_counter_torque_frames = 0
|
||||
|
||||
def _advance_lane_change_machine(self, one_blinker: bool, nav_exit_active: bool, below_speed: bool, lane_change_prob: float) -> None:
|
||||
if self._driver_cancels_lane_change():
|
||||
self._cancel_lane_change()
|
||||
return
|
||||
if self.lane_change_state == LaneChangeState.off:
|
||||
self._begin_from_idle(one_blinker, nav_exit_active, below_speed)
|
||||
return
|
||||
if self.lane_change_state == LaneChangeState.preLaneChange:
|
||||
self._step_pre_lane_change(one_blinker, nav_exit_active, below_speed)
|
||||
return
|
||||
if self.lane_change_state == LaneChangeState.laneChangeStarting:
|
||||
self._step_lane_change_starting(lane_change_prob)
|
||||
return
|
||||
if self.lane_change_state == LaneChangeState.laneChangeFinishing:
|
||||
self._step_lane_change_finishing(one_blinker, nav_exit_active)
|
||||
|
||||
def _update_timer(self) -> None:
|
||||
if self.lane_change_state in (LaneChangeState.off, LaneChangeState.preLaneChange):
|
||||
self.lane_change_timer = 0.0
|
||||
else:
|
||||
self.lane_change_timer += DT_MDL
|
||||
|
||||
def _clear_turn_desire_pulse(self) -> None:
|
||||
self.turn_desire_frames = 0
|
||||
self.turn_desire_cycle_input = log.Desire.none
|
||||
self.turn_heading_rad = 0.0
|
||||
self.turn_desire_seconds = 0.0
|
||||
self.turn_recoiled = False
|
||||
|
||||
def _measured_yaw_rate(self) -> float:
|
||||
# carState.yawRate is left unpopulated by many ports, so the model pose is the primary source
|
||||
rate = getattr(getattr(getattr(self, "_last_modeldata", None), "orientationRate", None), "z", None)
|
||||
try:
|
||||
if rate is not None and len(rate) and math.isfinite(rate[0]):
|
||||
return float(rate[0])
|
||||
except (TypeError, IndexError):
|
||||
pass
|
||||
try:
|
||||
return float(getattr(self._last_carstate, "yawRate", 0.0) or 0.0)
|
||||
except (TypeError, ValueError):
|
||||
return 0.0
|
||||
|
||||
def _turn_recoiled(self, desired_output: log.Desire) -> bool:
|
||||
# blinkers keep running well past the apex, so the executed heading change has to end the
|
||||
# desire: one that outlives the turn keeps steering the plan into the far side of the road
|
||||
if desired_output != self.turn_desire_cycle_input:
|
||||
self.turn_heading_rad = 0.0
|
||||
self.turn_desire_seconds = 0.0
|
||||
self.turn_recoiled = False
|
||||
self.turn_heading_rad += abs(self._measured_yaw_rate()) * DT_MDL
|
||||
# a red light at the junction must not use up the turn before the car moves
|
||||
if float(getattr(self._last_carstate, "vEgo", 0.0) or 0.0) > TURN_DESIRE_MOVING_SPEED:
|
||||
self.turn_desire_seconds += DT_MDL
|
||||
if self.turn_heading_rad >= TURN_RECOIL_HEADING_RAD or self.turn_desire_seconds >= TURN_DESIRE_MAX_SECONDS:
|
||||
self.turn_recoiled = True
|
||||
return self.turn_recoiled
|
||||
|
||||
def _pulse_turn_desire(self, desired_output: log.Desire) -> log.Desire:
|
||||
if desired_output not in _PULSED_TURN_DESIRES:
|
||||
self._clear_turn_desire_pulse()
|
||||
return desired_output
|
||||
|
||||
recoiled = self._turn_recoiled(desired_output)
|
||||
|
||||
if desired_output != self.turn_desire_cycle_input:
|
||||
self.turn_desire_frames = 0
|
||||
self.turn_desire_cycle_input = desired_output
|
||||
|
||||
cycle_frame = self.turn_desire_frames % TURN_DESIRE_PULSE_CYCLE_FRAMES
|
||||
self.turn_desire_frames += 1
|
||||
if recoiled or cycle_frame >= TURN_DESIRE_PULSE_HOLD_FRAMES:
|
||||
return log.Desire.none
|
||||
return desired_output
|
||||
|
||||
def _pick_desire_output(self) -> None:
|
||||
desired_output = log.Desire.none
|
||||
if self.nav_turn_direction != TurnDirection.none:
|
||||
desired_output = turn_desire(self.nav_turn_direction)
|
||||
elif self.lane_turn_direction != TurnDirection.none:
|
||||
desired_output = turn_desire(self.lane_turn_direction)
|
||||
else:
|
||||
desired_output = _LANE_CHANGE_DESIRES[(self.lane_change_direction, self.lane_change_state)]
|
||||
|
||||
self.desire = self._pulse_turn_desire(desired_output)
|
||||
|
||||
def update(self, carstate, lateral_active, lane_change_prob, nav_state=None, modeldata=None, radar_state=None):
|
||||
self._last_carstate = carstate
|
||||
self._last_modeldata = modeldata
|
||||
self.lateral_edge_guard.update(modeldata, carstate.vEgo, DT_MDL)
|
||||
self.lateral_edge_block = LateralEdgeBlock.none
|
||||
one_blinker = carstate.leftBlinker != carstate.rightBlinker
|
||||
below_speed = carstate.vEgo < LANE_CHANGE_SPEED_MIN
|
||||
nav_exit_active = self._refresh_turn_overrides(carstate, nav_state)
|
||||
if not nav_exit_active:
|
||||
self.nav_exit_rearmed = False
|
||||
|
||||
self.alc.update_params()
|
||||
if self._reset_required(lateral_active, nav_exit_active):
|
||||
self._clear_lane_change()
|
||||
else:
|
||||
self._advance_lane_change_machine(one_blinker, nav_exit_active, below_speed, lane_change_prob)
|
||||
|
||||
self._update_timer()
|
||||
self.prev_one_blinker = one_blinker and lateral_active
|
||||
self.prev_nav_exit_active = nav_exit_active
|
||||
self.alc.update_state()
|
||||
self._pick_desire_output()
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
|
||||
|
||||
try:
|
||||
load_private_module(__name__, "iqpilot_private.context.aux_inputs")
|
||||
except ProprietaryModuleMissing:
|
||||
class AuxModelInputs:
|
||||
def __init__(self, input_spec: dict):
|
||||
self.active = False
|
||||
|
||||
def poll(self) -> None:
|
||||
pass
|
||||
|
||||
def extras(self) -> dict:
|
||||
return {}
|
||||
+29
-8
@@ -23,7 +23,7 @@ from iqpilot.common.realtime import DT_MDL, config_realtime_process
|
||||
from iqpilot.common.swaglog import cloudlog
|
||||
from iqpilot.common.transformations.camera import DEVICE_CAMERAS
|
||||
from iqpilot.common.transformations.model import get_warp_matrix
|
||||
from iqpilot.selfdrive.controls.lib.desire_helper import DesireHelper
|
||||
from iqpilot.selfdrive.controls.lib.action_engine import ActionEngine
|
||||
from iqpilot.selfdrive.controls.lib.drive_helpers import (
|
||||
MODEL_SMOOTHING_MAX_TOTAL_SEC,
|
||||
dynamic_lat_smooth_extra_seconds,
|
||||
@@ -34,7 +34,7 @@ from iqpilot.selfdrive.locationd.calibration_helpers import get_calibrated_rpy
|
||||
from iqpilot.system import sentry
|
||||
|
||||
from iqpilot.common.steer_delay import lateral_action_delay
|
||||
from iqpilot.selfdrive.iqmodeld.models.helpers import get_active_bundle
|
||||
from iqpilot.selfdrive.iqmodeld.models.helpers import get_active_bundle, load_default_model_session, forced_active_bundle
|
||||
from iqpilot.selfdrive.iqmodeld.models.inference_state import InferenceStateBase
|
||||
from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import get_model_runner
|
||||
from iqpilot.selfdrive.iqmodeld.camera import CameraOffsetHelper
|
||||
@@ -243,13 +243,30 @@ def _merged_plan(runtime_state: "NeuralEngineState", outputs: dict[str, np.ndarr
|
||||
return base_plan + (runtime_state.PLANPLUS_CONTROL * _planplus_gain(vehicle_speed)) * outputs["planplus"][0]
|
||||
|
||||
|
||||
def _set_default_fallback_alert(active: bool) -> None:
|
||||
try:
|
||||
from iqpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
|
||||
set_offroad_alert("Offroad_ModelFallbackDefault", active)
|
||||
except Exception:
|
||||
cloudlog.exception("could not update the default-model fallback alert")
|
||||
|
||||
|
||||
class NeuralEngineState(InferenceStateBase):
|
||||
frames: dict[str, RoadProjector]
|
||||
|
||||
def __init__(self, gpu_context: WarpContext):
|
||||
super().__init__()
|
||||
runner = get_model_runner()
|
||||
bundle = get_active_bundle()
|
||||
try:
|
||||
runner = get_model_runner()
|
||||
bundle = get_active_bundle()
|
||||
_set_default_fallback_alert(False)
|
||||
except Exception:
|
||||
cloudlog.exception("active model failed to load; running the default model this session so pose stays valid")
|
||||
default_bundle = load_default_model_session()
|
||||
with forced_active_bundle(default_bundle):
|
||||
runner = get_model_runner()
|
||||
bundle = get_active_bundle()
|
||||
_set_default_fallback_alert(True)
|
||||
|
||||
self.model_runner = runner
|
||||
self.constants = runner.constants
|
||||
@@ -354,7 +371,10 @@ class NeuralEngineState(InferenceStateBase):
|
||||
|
||||
def get_action_from_model(self, outputs: dict[str, np.ndarray], previous_action: log.ModelDataV2.Action,
|
||||
lat_action_t: float, long_action_t: float, vehicle_speed: float,
|
||||
lat_smooth_seconds: float | None = None) -> log.ModelDataV2.Action:
|
||||
lat_smooth_seconds: float | None = None, *,
|
||||
lat_active: bool = True, current_curvature: float = 0.0) -> log.ModelDataV2.Action:
|
||||
if not lat_active:
|
||||
previous_action.desiredCurvature = current_curvature
|
||||
if lat_smooth_seconds is None:
|
||||
lat_smooth_seconds = self.LAT_SMOOTH_SECONDS
|
||||
|
||||
@@ -568,7 +588,7 @@ class InferenceDaemon:
|
||||
self._pub = PubMaster(pub_services)
|
||||
self._sub = SubMaster([
|
||||
"deviceState", "carState", "roadCameraState", "extrinsicsCalibration",
|
||||
"driverMonitoringState", "carControl", "lateralDelay", "iqNavState", "navInstruction", "radarState",
|
||||
"driverMonitoringState", "carControl", "controlsState", "lateralDelay", "iqNavState", "navInstruction", "radarState",
|
||||
] + self._feature_inputs.services)
|
||||
self._message_memory = DrivePacketMemory()
|
||||
self._params = Params()
|
||||
@@ -580,7 +600,7 @@ class InferenceDaemon:
|
||||
self._car_params = self._load_car_params(demo)
|
||||
self._long_action_delay = self._car_params.longitudinalActuatorDelay + self._runtime.LONG_SMOOTH_SECONDS
|
||||
self._previous_action = log.ModelDataV2.Action()
|
||||
self._desire_logic = DesireHelper()
|
||||
self._desire_logic = ActionEngine()
|
||||
self._lat_smooth_extra_sec = 0.0
|
||||
self._drive_profile_state = DriveProfileState()
|
||||
self._memory_resets = MemoryResetTracker()
|
||||
@@ -664,7 +684,8 @@ class InferenceDaemon:
|
||||
)
|
||||
lat_smooth_total_sec = min(self._runtime.LAT_SMOOTH_SECONDS + self._lat_smooth_extra_sec, MODEL_SMOOTHING_MAX_TOTAL_SEC)
|
||||
action = self._runtime.get_action_from_model(
|
||||
outputs, self._previous_action, lat_horizon, long_horizon, vehicle_speed, lat_smooth_total_sec
|
||||
outputs, self._previous_action, lat_horizon, long_horizon, vehicle_speed, lat_smooth_total_sec,
|
||||
lat_active=self._sub["carControl"].latActive, current_curvature=self._sub["controlsState"].curvature,
|
||||
)
|
||||
self._previous_action = action
|
||||
|
||||
|
||||
+4
-1
@@ -17,7 +17,10 @@ DESIRE_LEN = 8
|
||||
|
||||
def get_action_from_model(outputs: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
|
||||
v_ego: float, lat_action_t: float, long_action_t: float,
|
||||
lat_smooth_seconds: float | None = None) -> log.ModelDataV2.Action:
|
||||
lat_smooth_seconds: float | None = None, *,
|
||||
lat_active: bool = True, current_curvature: float = 0.0) -> log.ModelDataV2.Action:
|
||||
if not lat_active:
|
||||
prev_action.desiredCurvature = current_curvature
|
||||
if "action" in outputs:
|
||||
desired_accel = float(outputs["action"][0, 1])
|
||||
desired_curvature = float(outputs["action"][0, 0]) / (max(1.0, v_ego)) ** 2
|
||||
|
||||
+34
-1
@@ -139,7 +139,18 @@ def egpu_oob_pkl_path(meta: dict) -> str:
|
||||
|
||||
def egpu_model_oob_pkl_path(meta: dict) -> str:
|
||||
from iqpilot.system.hardware.hw import Paths
|
||||
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_model_oob.pkl")
|
||||
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_model_oob2.pkl")
|
||||
|
||||
|
||||
def drop_frozen_model_oob(meta: dict) -> None:
|
||||
from iqpilot.system.hardware.hw import Paths
|
||||
# warp-on-dock bundles saved under the old name were built before the prune fix and replay capture-time feeds
|
||||
stale = os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_model_oob.pkl")
|
||||
try:
|
||||
os.remove(stale)
|
||||
cloudlog.warning(f"removed frozen-feed warp-on-dock artifact {stale}")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def onnx_cache_path(meta: dict) -> str:
|
||||
@@ -288,6 +299,28 @@ def patch_tinygrad_fetch_fw() -> None:
|
||||
helpers.fetch_fw = fetch_fw
|
||||
|
||||
|
||||
def patch_tinygrad_prune_views() -> None:
|
||||
from tinygrad.engine import jit
|
||||
from tinygrad.uop.ops import Ops
|
||||
if getattr(jit.prune_linear, "_iq_patched", False):
|
||||
return
|
||||
|
||||
# upstream prune only sees bare BUFFER args, so a kernel reading an input through a SLICE view ran once at capture and froze that input
|
||||
def prune_linear(linear, needed):
|
||||
kept, onetime = [], []
|
||||
for si in linear.src:
|
||||
si_bufs = {b for src in si.src[1:] for b in src.toposort() if b.op is Ops.BUFFER}
|
||||
if not si_bufs.isdisjoint(needed):
|
||||
kept.append(si)
|
||||
needed |= si_bufs
|
||||
else:
|
||||
onetime.append(si)
|
||||
return linear.replace(src=tuple(kept)), linear.replace(src=tuple(onetime))
|
||||
|
||||
prune_linear._iq_patched = True
|
||||
jit.prune_linear = prune_linear
|
||||
|
||||
|
||||
def patch_usb_signal_wait() -> None:
|
||||
import time
|
||||
|
||||
|
||||
+44
-57
@@ -3,7 +3,6 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
@@ -24,26 +23,29 @@ RANGE_RETRIES = 4
|
||||
PARALLEL_PARTS = max(1, int(os.environ.get("IQ_DOWNLOAD_PARTS", "3")))
|
||||
|
||||
|
||||
def _requests_auth():
|
||||
def _git_auth():
|
||||
import importlib
|
||||
for mod in ("iqpilot_private.models.git_auth", "iqpilot.models_private_src.git_auth",
|
||||
"iqpilot.selfdrive.iqmodeld.models.git_auth"):
|
||||
try:
|
||||
return importlib.import_module(mod).get_requests_auth()
|
||||
return importlib.import_module(mod)
|
||||
except Exception:
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def _hf():
|
||||
import importlib
|
||||
for mod in ("iqpilot_private.models.git_auth", "iqpilot.selfdrive.iqmodeld.models.git_auth"):
|
||||
try:
|
||||
m = importlib.import_module(mod)
|
||||
return m.get_hf_headers(), m.hf_resolve_url
|
||||
except Exception:
|
||||
continue
|
||||
return None, None
|
||||
def _resolve_hf(hf_path: str) -> tuple[str | None, dict]:
|
||||
m = _git_auth()
|
||||
if m is None:
|
||||
return None, {}
|
||||
return m.resolve_hf(hf_path)
|
||||
|
||||
|
||||
def requests_get(url: str, **kwargs):
|
||||
m = _git_auth()
|
||||
if m is None:
|
||||
raise RuntimeError("no asset broker available")
|
||||
return m.requests_get(url, **kwargs)
|
||||
|
||||
|
||||
class RangesNotHonoured(Exception):
|
||||
@@ -189,10 +191,9 @@ def _verify_file(tmp: str, sha256: str, size: int) -> None:
|
||||
|
||||
def download_hf_file(hf_path: str, dst: str, sha256: str, size: int, progress_cb=None) -> str:
|
||||
import requests
|
||||
headers, resolve = _hf()
|
||||
if resolve is None:
|
||||
url, headers = _resolve_hf(hf_path)
|
||||
if url is None:
|
||||
raise RuntimeError("no HF credentials available")
|
||||
url = resolve(hf_path)
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
tmp = dst + ".hfpart"
|
||||
last_error: Exception | None = None
|
||||
@@ -213,8 +214,10 @@ def download_hf_file(hf_path: str, dst: str, sha256: str, size: int, progress_cb
|
||||
os.remove(leftover)
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
for _attempt in range(STREAM_RETRIES):
|
||||
for attempt in range(STREAM_RETRIES):
|
||||
try:
|
||||
if attempt:
|
||||
url, headers = _resolve_hf(hf_path)
|
||||
have = os.path.getsize(tmp) if os.path.isfile(tmp) else 0
|
||||
if size and have > size:
|
||||
os.remove(tmp)
|
||||
@@ -242,26 +245,6 @@ def download_hf_file(hf_path: str, dst: str, sha256: str, size: int, progress_cb
|
||||
raise RuntimeError(f"HF download failed: {last_error}")
|
||||
|
||||
|
||||
def _lfs_endpoint(base_url: str) -> str:
|
||||
return base_url.split("/raw/", 1)[0] + ".git/info/lfs"
|
||||
|
||||
|
||||
def _resolve_oid(session, base_url: str, oid: str, size: int, auth):
|
||||
import requests
|
||||
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"},
|
||||
auth=auth, 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 _part_path(dst: str, oid: str) -> str:
|
||||
return os.path.join(dst + ".parts", oid)
|
||||
|
||||
@@ -276,19 +259,28 @@ def _part_complete(path: str, oid: str, size: int) -> bool:
|
||||
return digest.hexdigest() == oid
|
||||
|
||||
|
||||
def _fetch_part(session, base_url: str, obj: dict, path: str, auth, progress) -> None:
|
||||
def _fetch_part(session, obj: dict, path: str, progress) -> None:
|
||||
size = int(obj["size"])
|
||||
have = os.path.getsize(path) if os.path.isfile(path) else 0
|
||||
if have > size:
|
||||
os.remove(path)
|
||||
have = 0
|
||||
href, headers = _resolve_oid(session, base_url, obj["oid"], size, auth)
|
||||
obj_auth = None if headers.get("Authorization") else auth
|
||||
m = _git_auth()
|
||||
if m is None:
|
||||
raise RuntimeError("no asset broker available for LFS parts")
|
||||
target = m.broker_lfs_url(obj["oid"], size)
|
||||
headers = m.broker_headers() if target else {}
|
||||
if not (target and headers):
|
||||
raise RuntimeError("no device identity for the konn3kt asset broker")
|
||||
_stream_part(session, target, headers, path, have, progress)
|
||||
|
||||
|
||||
def _stream_part(session, href: str, headers: dict, path: str, have: int, progress) -> None:
|
||||
# LFS parts are content-addressed (oid == sha256), so a half-written part can be resumed with a
|
||||
# Range request and verified afterwards instead of being thrown away on every restart.
|
||||
if have:
|
||||
headers = {**headers, "Range": f"bytes={have}-"}
|
||||
with session.get(href, headers=headers, stream=True, timeout=HTTP_TIMEOUT_S, auth=obj_auth) as r:
|
||||
with session.get(href, headers=headers, stream=True, timeout=HTTP_TIMEOUT_S) as r:
|
||||
r.raise_for_status()
|
||||
if have and r.status_code != 206:
|
||||
have = 0
|
||||
@@ -300,7 +292,6 @@ def _fetch_part(session, base_url: str, obj: dict, path: str, auth, progress) ->
|
||||
|
||||
def download_lfs_bundle(objects: list, dst: str, sha256: str, size: int, progress_cb=None) -> str:
|
||||
import requests
|
||||
auth = _requests_auth()
|
||||
os.makedirs(dst + ".parts", exist_ok=True)
|
||||
total = int(size) or sum(int(o["size"]) for o in objects)
|
||||
done_bytes = sum(int(o["size"]) for o in objects if _part_complete(_part_path(dst, o["oid"]), o["oid"], int(o["size"])))
|
||||
@@ -318,30 +309,26 @@ def download_lfs_bundle(objects: list, dst: str, sha256: str, size: int, progres
|
||||
with lock:
|
||||
progress(n)
|
||||
|
||||
def fetch(base_url: str, obj: dict) -> None:
|
||||
def fetch(obj: dict) -> None:
|
||||
path = _part_path(dst, obj["oid"])
|
||||
if _part_complete(path, obj["oid"], int(obj["size"])):
|
||||
return
|
||||
_fetch_part(requests.Session(), base_url, obj, path, auth, locked_progress)
|
||||
_fetch_part(requests.Session(), obj, path, locked_progress)
|
||||
if not _part_complete(path, obj["oid"], int(obj["size"])):
|
||||
if os.path.getsize(path) >= int(obj["size"]):
|
||||
os.remove(path)
|
||||
raise RuntimeError(f"part {obj['oid'][:12]} incomplete or failed verification")
|
||||
|
||||
for base_url in MODELS_BASE_URLS:
|
||||
for _attempt in range(STREAM_RETRIES):
|
||||
try:
|
||||
pending = [o for o in objects if not _part_complete(_part_path(dst, o["oid"]), o["oid"], int(o["size"]))]
|
||||
got[0] = total - sum(int(o["size"]) for o in pending)
|
||||
with ThreadPoolExecutor(max_workers=max(1, min(PARALLEL_PARTS, len(pending) or 1))) as pool:
|
||||
for _ in pool.map(functools.partial(fetch, base_url), pending):
|
||||
pass
|
||||
break
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
else:
|
||||
continue
|
||||
break
|
||||
for _attempt in range(STREAM_RETRIES):
|
||||
try:
|
||||
pending = [o for o in objects if not _part_complete(_part_path(dst, o["oid"]), o["oid"], int(o["size"]))]
|
||||
got[0] = total - sum(int(o["size"]) for o in pending)
|
||||
with ThreadPoolExecutor(max_workers=max(1, min(PARALLEL_PARTS, len(pending) or 1))) as pool:
|
||||
for _ in pool.map(fetch, pending):
|
||||
pass
|
||||
break
|
||||
except Exception as e:
|
||||
last_error = e
|
||||
else:
|
||||
raise RuntimeError(f"model bundle download failed: {last_error}")
|
||||
|
||||
|
||||
+5
-3
@@ -17,7 +17,8 @@ _OVERRIDE_KEYS = (
|
||||
"combinedSplitArtifact",
|
||||
"iqCombinedArtifact",
|
||||
)
|
||||
_SPLIT_ROLE_PATTERN = re.compile(r"^driving_(vision|policy|off_policy|on_policy)_(.+)_tinygrad\.pkl$")
|
||||
_SPLIT_ROLE_PATTERN = re.compile(r"^driving_(vision|policy|off_policy|on_policy)_(.+)_tinygrad\.pkl(?:\.iqc)?$")
|
||||
_IQC_SUFFIX = ".iqc"
|
||||
|
||||
|
||||
def _bundle_models(bundle) -> list:
|
||||
@@ -91,8 +92,9 @@ def combined_split_artifact_candidates(bundle) -> list[Path]:
|
||||
|
||||
def resolve_combined_split_artifact(bundle) -> Path | None:
|
||||
for candidate in combined_split_artifact_candidates(bundle):
|
||||
if candidate.is_file():
|
||||
return candidate
|
||||
for path in (candidate, candidate.with_name(candidate.name + _IQC_SUFFIX)):
|
||||
if path.is_file():
|
||||
return path
|
||||
return None
|
||||
|
||||
|
||||
|
||||
+33
@@ -305,7 +305,40 @@ def get_runtime_bundle_upgrade(bundle, params: Params = None, available_bundles=
|
||||
return _find_runtime_upgrade(bundle, params, available_bundles)
|
||||
|
||||
|
||||
_forced_bundle = None
|
||||
|
||||
|
||||
def load_default_model_session():
|
||||
"""Stage the shipped default model files and return a decoded default bundle
|
||||
WITHOUT persisting it as the active selection, so the next boot retries the
|
||||
user's own model instead of staying on the default forever."""
|
||||
bundle_dict = _load_default_bundle_dict()
|
||||
ensure_default_model_files(bundle_dict)
|
||||
return ModelBundle(**bundle_dict)
|
||||
|
||||
|
||||
class forced_active_bundle:
|
||||
"""Make get_active_bundle return `bundle` for the duration of the block, used to
|
||||
build a runner for a session-only fallback bundle without touching Params."""
|
||||
def __init__(self, bundle):
|
||||
self._bundle = bundle
|
||||
|
||||
def __enter__(self):
|
||||
global _forced_bundle
|
||||
self._prev = _forced_bundle
|
||||
_forced_bundle = self._bundle
|
||||
return self._bundle
|
||||
|
||||
def __exit__(self, *exc):
|
||||
global _forced_bundle
|
||||
_forced_bundle = self._prev
|
||||
return False
|
||||
|
||||
|
||||
def get_active_bundle(params: Params = None):
|
||||
if _forced_bundle is not None:
|
||||
return _forced_bundle
|
||||
|
||||
params = Params() if params is None else params
|
||||
|
||||
try:
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
|
||||
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
|
||||
|
||||
try:
|
||||
load_private_module(__name__, "iqpilot_private.models.iqc")
|
||||
except ProprietaryModuleMissing:
|
||||
from iqpilot.models_private_src.iqc import * # noqa: F403
|
||||
+2
-3
@@ -2,7 +2,6 @@
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
import os
|
||||
import pickle as _pk
|
||||
from dataclasses import dataclass, field
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
@@ -13,6 +12,7 @@ from iqpilot.system.hardware import TICI
|
||||
from iqpilot.system.hardware.hw import Paths as _hw_paths
|
||||
from iqpilot.selfdrive.iqmodeld.models.helpers import get_active_bundle as _fetch_bundle
|
||||
from iqpilot.selfdrive.iqmodeld.models.combined_artifact import has_combined_split_artifact
|
||||
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
|
||||
|
||||
# ---- runtime type surface (native OpenCL/frame handles resolve to Any off-device) ----
|
||||
if TYPE_CHECKING:
|
||||
@@ -73,8 +73,7 @@ def load_artifact_metadata(metadata_filename):
|
||||
if os.path.getsize(path) > _META_MAX_BYTES:
|
||||
cloudlog.error(f"metadata pkl {metadata_filename} is artifact-sized, refusing to unpickle it")
|
||||
return tuple({} for _ in _META_FIELDS)
|
||||
with open(path, 'rb') as fh:
|
||||
blob = _pk.load(fh)
|
||||
blob = load_pickle(path)
|
||||
return tuple(blob.get(field, {}) for field in _META_FIELDS)
|
||||
except Exception:
|
||||
cloudlog.exception(f"unreadable metadata pkl {metadata_filename}, continuing without it")
|
||||
|
||||
+2
-3
@@ -4,7 +4,6 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pickle
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
@@ -15,6 +14,7 @@ from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import NumpyDict, Sh
|
||||
from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import ModelRunner
|
||||
from iqpilot.selfdrive.iqmodeld.models.split_model_constants import SplitModelConstants
|
||||
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
|
||||
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
|
||||
|
||||
|
||||
def _tinygrad_imports():
|
||||
@@ -64,8 +64,7 @@ class TinygradCombinedSplitRunner(ModelRunner):
|
||||
if self._artifact_path is None:
|
||||
raise FileNotFoundError("No IQ combined split artifact is available for the active bundle")
|
||||
|
||||
with open(self._artifact_path, "rb") as artifact:
|
||||
runtime_package: dict[Any, Any] = pickle.load(artifact)
|
||||
runtime_package: dict[Any, Any] = load_pickle(self._artifact_path)
|
||||
|
||||
self._meta_by_role = runtime_package.get("meta_by_role", runtime_package.get("metadata", {}))
|
||||
self._policy_roles = runtime_package.get("roles", _phase_roles(self._meta_by_role))
|
||||
|
||||
+2
-3
@@ -4,7 +4,6 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pickle
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
@@ -15,6 +14,7 @@ from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import (
|
||||
from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import ModelRunner
|
||||
from iqpilot.selfdrive.iqmodeld.models.split_model_constants import SplitModelConstants
|
||||
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
|
||||
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
|
||||
|
||||
|
||||
def _tinygrad_imports():
|
||||
@@ -40,8 +40,7 @@ class TinygradFusedRunner(ModelRunner):
|
||||
self._model_data = next(iter(self.models.values()))
|
||||
|
||||
pkl_path = os.path.join(CUSTOM_MODEL_PATH, self._model_data.model.artifact.fileName)
|
||||
with open(pkl_path, 'rb') as f:
|
||||
self._fused: dict[Any, Any] = pickle.load(f)
|
||||
self._fused: dict[Any, Any] = load_pickle(pkl_path)
|
||||
|
||||
self._vision_meta = self._fused['metadata']['vision']
|
||||
self._on_meta = self._fused['metadata']['on_policy']
|
||||
|
||||
+2
-3
@@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
@@ -17,6 +16,7 @@ from iqpilot.selfdrive.iqmodeld.models.runners.model_runner import ModelRunner
|
||||
from iqpilot.selfdrive.iqmodeld.models.split_model_constants import SplitModelConstants
|
||||
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
|
||||
from iqpilot.selfdrive.iqmodeld.models.runners.tinygrad.supercombo_runtime import TinygradSupercomboRuntime
|
||||
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
|
||||
|
||||
|
||||
def _captured_queue_depth(warp_jit: Any) -> int | None:
|
||||
@@ -77,8 +77,7 @@ class TinygradSupercomboRunner(TinygradSupercomboRuntime, ModelRunner):
|
||||
self._pkl_path = pkl_path
|
||||
self._expected_sha256 = getattr(getattr(self._model_data.model.artifact, "downloadUri", None), "sha256", "") or ""
|
||||
self._verify_artifact_file()
|
||||
with open(pkl_path, 'rb') as f:
|
||||
self._m: dict[Any, Any] = pickle.load(f)
|
||||
self._m: dict[Any, Any] = load_pickle(pkl_path)
|
||||
|
||||
self._meta = self._m['metadata']
|
||||
self._ish = self._meta['input_shapes']
|
||||
|
||||
+7
-8
@@ -4,7 +4,6 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pickle
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
@@ -32,6 +31,7 @@ from iqpilot.selfdrive.iqmodeld.models.split_model_constants import SplitModelCo
|
||||
from iqpilot.selfdrive.iqmodeld.config import ModelConstants
|
||||
from iqpilot.selfdrive.iqmodeld.runtime.tinygrad import qcom_tensor_from_opencl_address
|
||||
from iqpilot.system.hardware import TICI
|
||||
from iqpilot.selfdrive.iqmodeld.models.iqc import load_pickle
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -45,12 +45,11 @@ def _artifact_path(filename: str) -> str:
|
||||
|
||||
|
||||
def _load_program_blob(filename: str):
|
||||
with open(_artifact_path(filename), "rb") as artifact:
|
||||
try:
|
||||
return pickle.load(artifact)
|
||||
except FileNotFoundError as exc:
|
||||
assert "/dev/kgsl-3d0" not in str(exc), "Model was built on C3 or C3X, but is being loaded on PC"
|
||||
raise
|
||||
try:
|
||||
return load_pickle(_artifact_path(filename))
|
||||
except FileNotFoundError as exc:
|
||||
assert "/dev/kgsl-3d0" not in str(exc), "Model was built on C3 or C3X, but is being loaded on PC"
|
||||
raise
|
||||
|
||||
|
||||
def _compile_input_plan(captured) -> dict[str, _TensorShapePlan]:
|
||||
@@ -81,7 +80,7 @@ class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTiny
|
||||
raise ValueError(f"Model data for type {model_type} not available.")
|
||||
|
||||
asset_name = self._model_data.model.artifact.fileName
|
||||
assert asset_name.endswith("_tinygrad.pkl"), f"Invalid model file {asset_name} for TinygradRunner"
|
||||
assert asset_name.removesuffix(".iqc").endswith("_tinygrad.pkl"), f"Invalid model file {asset_name} for TinygradRunner"
|
||||
|
||||
self.model_run = _load_program_blob(asset_name)
|
||||
self._input_plan = _compile_input_plan(self.model_run.captured)
|
||||
|
||||
+21
-5
@@ -125,16 +125,32 @@ class Amplifier:
|
||||
print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}")
|
||||
|
||||
def set_configs(self, configs: list[AmpConfig]) -> bool:
|
||||
import threading
|
||||
|
||||
tries = 15
|
||||
backoff = 0.
|
||||
for i in range(tries):
|
||||
try:
|
||||
self._set_configs(configs)
|
||||
return True
|
||||
except OSError:
|
||||
result = [None]
|
||||
error = [None]
|
||||
def _worker():
|
||||
try:
|
||||
self._set_configs(configs)
|
||||
result[0] = True
|
||||
except OSError as e:
|
||||
error[0] = e
|
||||
t = threading.Thread(target=_worker, daemon=True)
|
||||
t.start()
|
||||
t.join(timeout=2)
|
||||
if t.is_alive():
|
||||
backoff += 0.1
|
||||
time.sleep(backoff)
|
||||
print(f"Failed to set amp config, {tries - i - 1} retries left")
|
||||
print(f"Failed to set amp config (timeout), {tries - i - 1} retries left")
|
||||
continue
|
||||
if result[0]:
|
||||
return True
|
||||
backoff += 0.1
|
||||
time.sleep(backoff)
|
||||
print(f"Failed to set amp config, {tries - i - 1} retries left")
|
||||
return False
|
||||
|
||||
def set_global_shutdown(self, amp_disabled: bool) -> bool:
|
||||
|
||||
+8
-6
@@ -469,8 +469,10 @@ class Tici(HardwareBase):
|
||||
if self.amplifier is not None:
|
||||
self.amplifier.initialize_configuration(self.get_device_type())
|
||||
|
||||
# Allow hardwared to write engagement status to kmsg
|
||||
os.system("sudo chmod a+w /dev/kmsg")
|
||||
try:
|
||||
subprocess.run(["sudo", "-n", "chmod", "a+w", "/dev/kmsg"], timeout=2, capture_output=True, check=False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Ensure fan gpio is enabled so fan runs until shutdown, also turned on at boot by the ABL
|
||||
gpio_init(GPIO.SOM_ST_IO, True)
|
||||
@@ -514,10 +516,10 @@ class Tici(HardwareBase):
|
||||
affine_irq(3, "xhci-hcd:usb1")
|
||||
try:
|
||||
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
|
||||
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
|
||||
subprocess.call(["sudo", "taskset", "-pc", "3", pid], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except subprocess.CalledProcessException as e:
|
||||
print(str(e))
|
||||
subprocess.run(["sudo", "-n", "chrt", "-f", "-p", "1", pid], timeout=2, capture_output=True, check=False)
|
||||
subprocess.run(["sudo", "-n", "taskset", "-pc", "3", pid], timeout=2, capture_output=True, check=False)
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
def configure_modem(self):
|
||||
from iqpilot.common.params import Params
|
||||
|
||||
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
Executable → Regular
BIN
Binary file not shown.
@@ -1 +0,0 @@
|
||||
f217b0f040cfa83f51d6e935f1646946f5992b58
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"bundle_version": 1792395046,
|
||||
"python/_iqclosure/_vendored.json": {
|
||||
"mode": 420,
|
||||
"sha256": "8cbd4406c95aeca668d07f6714cffdfc00d7143988901bfe86c1bb1381019996",
|
||||
"size": 2050
|
||||
"sha256": "93d71b5f76734cb6ceff3ead9218b21727bf47814a1a25d6be5a44994b9528fb",
|
||||
"size": 1717
|
||||
},
|
||||
"python/_iqclosure/iqpilot/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -16,34 +17,24 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "97d6e0d8cbd3e47797d734322b42d04efea4d95a2dae5a56f1baef3fd847aa0d",
|
||||
"size": 939
|
||||
"sha256": "1dd848ec6e867836a321a93f9ac218415c19528aa356a3e80969503285316b54",
|
||||
"size": 900
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/base.py": {
|
||||
"mode": 420,
|
||||
"sha256": "86b834e02e24a005c903f1ca059b1a572e37c17a87271726c90918cd04b6133b",
|
||||
"size": 3451
|
||||
"sha256": "5b312ec45e673d9337a89ea82a157152d1f64e5e48518807a592247787087609",
|
||||
"size": 4921
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/api/comma_connect.py": {
|
||||
"python/_iqclosure/iqpilot/common/api/konn3kt.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e7c2dd0b56fb8533c2cbc614e728a9ffc5ce7360524cfa62a02f5235bc23b9a8",
|
||||
"size": 267
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/atlas_alerts.py": {
|
||||
"mode": 420,
|
||||
"sha256": "a8b47e01f604d41d4959d4b0f5ed594c1ab90e5b0f7fb2962b5a4a71ec766598",
|
||||
"size": 9411
|
||||
"sha256": "2282e19909079bfd77a089f480df5a229191c35f8523f005d20e2ea468a6b0bc",
|
||||
"size": 313
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/basedir.py": {
|
||||
"mode": 420,
|
||||
"sha256": "58cfa5d6405903ee1acecc4544463bb8f47a66921b120c382143aa80d8539b28",
|
||||
"size": 106
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/constants.py": {
|
||||
"mode": 420,
|
||||
"sha256": "fc439d59c24b95ed349b84f1a5862df3ea50e0891dbec4ea3cf5e1e0ccff6513",
|
||||
"size": 434
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/file_helpers.py": {
|
||||
"mode": 420,
|
||||
"sha256": "3c661d05dea697fae741b3e65a93385e4e0f5941eae8e8f4ea64da8da90e2f52",
|
||||
@@ -64,11 +55,6 @@
|
||||
"sha256": "4af8f9da7504a548d15f18b91d71c1379cb7c05c84be031448764cc49920ce68",
|
||||
"size": 2449
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/issue_debug.py": {
|
||||
"mode": 420,
|
||||
"sha256": "6fadd02538b85f92963f247a43dcc6f50cf4c65cdc4171f3c7e03646a76fcd1b",
|
||||
"size": 1019
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/logging_extra.py": {
|
||||
"mode": 420,
|
||||
"sha256": "ce9b3b3d8498d76e1c4c0352a3e98e72759bb7f367ff8629c1329c46040595c3",
|
||||
@@ -79,11 +65,6 @@
|
||||
"sha256": "016be524079ef32df03b2c135eab8f83da60a46fc7f143620b836c65f33e39a3",
|
||||
"size": 4423
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/spinner.py": {
|
||||
"mode": 420,
|
||||
"sha256": "33a2eb2aa3148492414035c5dc7150b3a69467905d7a3e58c8f65cfe70d3552c",
|
||||
"size": 1371
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/swaglog.py": {
|
||||
"mode": 420,
|
||||
"sha256": "0a133b4ef49f80b8890f115e81273f3f3e21351ae79f92dc02abc033e6d24027",
|
||||
@@ -96,8 +77,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/utils.py": {
|
||||
"mode": 420,
|
||||
"sha256": "103fa6c24f8b963576c482a34d7ecb020168fd3e85156493ae4432ee3b7b2c06",
|
||||
"size": 5159
|
||||
"sha256": "c39b89a1ab34511dfa5f9efac783fde0e6d616963a416cf472bd1f604b57f84d",
|
||||
"size": 5892
|
||||
},
|
||||
"python/_iqclosure/iqpilot/common/version.py": {
|
||||
"mode": 420,
|
||||
@@ -116,8 +97,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/konn3kt/registration.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e9c37e8ae0a70c3a19ca1a33b290e94658b255fab153a941f50f8a29168d75c3",
|
||||
"size": 7816
|
||||
"sha256": "5a2b06d919adce513fb8a7e4e6f26d48e502e92dbd89f9af7a0e67e9114ab3be",
|
||||
"size": 2683
|
||||
},
|
||||
"python/_iqclosure/iqpilot/konn3kt/service_health.py": {
|
||||
"mode": 420,
|
||||
@@ -141,8 +122,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/car/vehicle_catalog.json": {
|
||||
"mode": 420,
|
||||
"sha256": "9226a2475a10088d7f472af2de9e9e3884522562db9432376c4a0def6142fe38",
|
||||
"size": 122493
|
||||
"sha256": "ac6ffed7e5374df5add04646af383980d251d400b0617755bd54a1fc85d21325",
|
||||
"size": 124301
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/car/vehicle_catalog.py": {
|
||||
"mode": 420,
|
||||
@@ -161,8 +142,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
|
||||
"mode": 420,
|
||||
"sha256": "a9f2bc8477b58a3df7196f4b40cdddf631e28051809e9bbfdd4c78148444c474",
|
||||
"size": 10891
|
||||
"sha256": "af83d52609ec4f2abe82f4e5f788ce4788bdc41cbf7b3de36dc63f1f598ef46c",
|
||||
"size": 12107
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
|
||||
"mode": 420,
|
||||
@@ -171,8 +152,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_bundle_downloader.py": {
|
||||
"mode": 420,
|
||||
"sha256": "83b0190f19e3f037c937a03b1988931729ed43d4dac92223aba0745697611ed8",
|
||||
"size": 12835
|
||||
"sha256": "62b13a05afbe9b5fb5d9e3d09c1491b37826ff840126c5e317a7ebf1edc28762",
|
||||
"size": 12060
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -186,48 +167,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/models/helpers.py": {
|
||||
"mode": 420,
|
||||
"sha256": "2fad977c3816ff824c126a92605f866f3bc0aa989ed8397e818bed481a41192f",
|
||||
"size": 14051
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/locationd/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/locationd/calibrationd.py": {
|
||||
"mode": 420,
|
||||
"sha256": "6bceebb5a15895d2b6d20a28cbd14b9112f9422e0a0cf466314a7e8779da39dc",
|
||||
"size": 15698
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/selfdrived/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/selfdrived/alertmanager.py": {
|
||||
"mode": 420,
|
||||
"sha256": "c601bac30951a750d3ebf3d30f06c597bacd148210b1c405d5d3eca79bb7c4e3",
|
||||
"size": 2045
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/selfdrived/events.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e1b113565c45271ec5310dd83940dd338ff7fa56a2bf121f864dd58cc0ba1343",
|
||||
"size": 32024
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/ui/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/ui/feedback/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/_iqclosure/iqpilot/selfdrive/ui/feedback/feedbackd.py": {
|
||||
"mode": 420,
|
||||
"sha256": "7c1e6cb2c3c2c6aae91210f6cbc5c372f33c28cc3edb292ef71439b83844615d",
|
||||
"size": 2615
|
||||
"sha256": "9628f88e71f7fae2c3d62dd17618367b3c7d12749cfe2a4737410288888cd3bd",
|
||||
"size": 15008
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -266,8 +207,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/amplifier.py": {
|
||||
"mode": 420,
|
||||
"sha256": "98f46f8705259b00efae7274186b4e68201aad467a19de4f16d5f850d10ab0b5",
|
||||
"size": 7585
|
||||
"sha256": "38e521356c56ac85f35543b9457d36639e55ed2ac916f30ea3b86948e806541f",
|
||||
"size": 8016
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/esim_manager.py": {
|
||||
"mode": 420,
|
||||
@@ -276,8 +217,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/hardware.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e8eae4e8a75e1751525471cf54a8555b7bfd7f08ad84bf2e75642b5745f222db",
|
||||
"size": 25601
|
||||
"sha256": "da5ecb280f7af14169903260dc941e3bfac033c93b184cb0abea0f1494c34582",
|
||||
"size": 25731
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/hardware/tici/iwlist.py": {
|
||||
"mode": 420,
|
||||
@@ -316,18 +257,13 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/manager/process.py": {
|
||||
"mode": 420,
|
||||
"sha256": "04bf7d945e43ed67362916884caf9ac6011ddd5dacc985f2adb282eacbdf225b",
|
||||
"size": 10130
|
||||
"sha256": "703663ef081e8786b0071df02d95b404b9236c6162d044a0766eb34ef3181875",
|
||||
"size": 9926
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/manager/process_config.py": {
|
||||
"mode": 420,
|
||||
"sha256": "f7a4f10168ce71e490578164c08328e1e983bd9e72df955c7e34d4eda6be7086",
|
||||
"size": 15379
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/micd.py": {
|
||||
"mode": 420,
|
||||
"sha256": "1a59cbe6b72fcca5723ab56dc09f52039817299071a8d8da37f04acea58776bc",
|
||||
"size": 5977
|
||||
"sha256": "5d5f9354ac81904097cd7c25f6ea9ee2c5dcf74ed63a3400530dddcc6a1f1660",
|
||||
"size": 15410
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/phonegps/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -366,8 +302,8 @@
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/ui/lib/wifi_manager.py": {
|
||||
"mode": 420,
|
||||
"sha256": "c9fe91484344f154726b0cab62a19f58a172e4f06dfd6341ca9b179c0c4646a8",
|
||||
"size": 41618
|
||||
"sha256": "859d3eb2f56eb456f1d6d2df18d93c5d4990a7f4a6e75be495930a19efcb5151",
|
||||
"size": 42121
|
||||
},
|
||||
"python/_iqclosure/iqpilot/system/version.py": {
|
||||
"mode": 420,
|
||||
@@ -400,29 +336,29 @@
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "7afc7118cb0e8fcfcdc552a7df224cd02dedda078d79e3484cc7c2c87209582c",
|
||||
"size": 135480
|
||||
"mode": 416,
|
||||
"sha256": "97137b00af89cc21ec2b9c07f5887304ff0a7379619b37837384c3e6d49a96a2",
|
||||
"size": 1130808
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "bd195bcc71dd7f15975ef2bbb28317fd7518f68c73a3d056d214b8b3446a8852",
|
||||
"size": 67680
|
||||
"mode": 416,
|
||||
"sha256": "0674dcaf72b8137be6c75ac220001f2ea440fea92c93c5211a01e0d513772df7",
|
||||
"size": 1065192
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "53fe8078e5388290dc36fd765c3d923b9617ac35cb7cb94c4faf8234afa69766",
|
||||
"size": 204616
|
||||
"mode": 416,
|
||||
"sha256": "3e8e9d8d2ac2282cda1bb02e7ae366c373191bd49c49de47e09effa2f000a4f2",
|
||||
"size": 1130968
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "c077e27d89e8b1704b4a179654690c2e0d2db08941bf8b55e511a3e524e88508",
|
||||
"size": 69752
|
||||
"mode": 416,
|
||||
"sha256": "2135582d5a3cedf8247dce06242619e90e1d519a44548891de071b7cab1ec694",
|
||||
"size": 1065144
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "b014f9ddb6ad90a40877ff1bcf74f00f493b8b128e32025aff876cae6458ab03",
|
||||
"size": 203064
|
||||
"mode": 416,
|
||||
"sha256": "d36c107d46199f3efc6f10b266f2658119212e184dbcdbecff09c450a6dfc381",
|
||||
"size": 1065368
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/flockd/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -430,14 +366,14 @@
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "3a066b70e0f2755f97fa9886389dd0e50392a9a01f0dc6c8eda2655cdf44d010",
|
||||
"size": 268240
|
||||
"mode": 416,
|
||||
"sha256": "c801140079340b2e4ab00ad925ad7bc8b5184ea7f19601643ad03f65c2dd5f09",
|
||||
"size": 1196424
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "15d8e8b6a620c41dc6fd597ff65e6545ba4da13297355a12efabca92107e1a23",
|
||||
"size": 68104
|
||||
"mode": 416,
|
||||
"sha256": "66129d17569ca6678ba81dd7bda5d06fd68c1a7a84c0b4e6a2a610cbaf640686",
|
||||
"size": 1065144
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -446,118 +382,133 @@
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/_vendor/localapi_runtime.zip": {
|
||||
"mode": 420,
|
||||
"sha256": "17a7e20afa7a49c43883f54a7b7f2dd0319eac0235a2d87ee1f6b70b37ef5af3",
|
||||
"size": 1379463
|
||||
"sha256": "8562471927037cfce082e59054ffcdd39ca67519e8bad3f2583418f28f898960",
|
||||
"size": 1379541
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "330b1a62d6ddc23a20c493945dcf1e68bb0144cfbe3b44f9d3e06c65ad438c02",
|
||||
"size": 268072
|
||||
"mode": 416,
|
||||
"sha256": "16a9c7dd5865cc62f67af7f7af931adc31a82a3292f4e952c153a68b3e557e47",
|
||||
"size": 1262120
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_connection_parameters.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "ae55af9d22773bcb5b497fdc7da051345341c2699590d61dae0a6ba0ab6b8072",
|
||||
"size": 68104
|
||||
"mode": 416,
|
||||
"sha256": "8ec415342cdb92e468f477f36117ec9e9e3ed3c4e10625a133fbf1b6ffc7db7b",
|
||||
"size": 999768
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "b16d70ff35a87b2707d00de69179d672f89a8cc8c355344fa0bd395df5a85d4f",
|
||||
"size": 472568
|
||||
"mode": 416,
|
||||
"sha256": "d940e5fa03e70641c613eb4d0a1392c4564d9393dee7226112564f6041ccc396",
|
||||
"size": 1327640
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "9f42acb2fd579d548f05eabd6f892a27ccd0b5224e052dbe0a1caa8bcae1e15a",
|
||||
"size": 68080
|
||||
"mode": 416,
|
||||
"sha256": "8cef60cba9aa38e086c585c786b60223d7d7b858a02ad88d19a1f434d73db9d9",
|
||||
"size": 1065272
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "9b3de54dc169aed94abed3620e5ddc58865317c1c4865777d249e0056e459cf8",
|
||||
"size": 402424
|
||||
"mode": 416,
|
||||
"sha256": "8e080bac97b176d2a7b271bedc3ac735c11079e8ac2a03cd2c19b4761155ccde",
|
||||
"size": 1393080
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "987e5afd63f14b691fbc313b4ccd55d03be8f378a06829dde4cbe05d5f9d6fbc",
|
||||
"size": 337616
|
||||
"mode": 416,
|
||||
"sha256": "bae356c2438f4c779b72c829deda319e043ce5792c624864a72b0883d6196d4a",
|
||||
"size": 1262056
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "c2e296298df67271d0a113fd36f5af5d9fe2dd645decaffe3b8af685c8e2266e",
|
||||
"size": 134424
|
||||
"mode": 416,
|
||||
"sha256": "a1bb5651a3f2bba40cca75070b721fb8c3e0108475e15227f4c2e218d201b4d0",
|
||||
"size": 1065320
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/driving_settings.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "3f3fd10b7394e9534738c3cfc2ae6c6a3dad8d0dd7953f009e46d017f66ef190",
|
||||
"size": 68152
|
||||
"mode": 416,
|
||||
"sha256": "e1184e9e8d5fee473e99e0edd504784d1edbbf4b5898f1eeb14794e25d2ccb7d",
|
||||
"size": 999800
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "d2f5d3004195e660b86639e4c82402b299204db9deb161558dad9f2907abf9ed",
|
||||
"size": 4311240
|
||||
"mode": 416,
|
||||
"sha256": "89a1ee80f07a7547ecc81e1293300969079095046b859fafd9a9871f5fe51687",
|
||||
"size": 5065800
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/iqsentryd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "fab6aec7f68e57b725ec68f049c23af734502b2bbd31e0c582ad8147ed9fa64c",
|
||||
"size": 402296
|
||||
"mode": 416,
|
||||
"sha256": "690af279a513a1491ec2639d6aa6bf391a5a690456458ab1c122e90664c0848f",
|
||||
"size": 1393128
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/iqsentryvisiond.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "df46250fca25ef5e87c50c3a0caad434dd92fa893f8c9e77300d331d63cb2fc5",
|
||||
"size": 271008
|
||||
"mode": 416,
|
||||
"sha256": "92a8bc5e07148a44920f90ed6de201c81011f77a7d38675ad8ae60ac263f3bde",
|
||||
"size": 1196504
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/k3wgd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "0430935f4dea6b17d1eea372e07203b74b2842cef69bcc149db5921b99ce5499",
|
||||
"size": 338912
|
||||
"mode": 416,
|
||||
"sha256": "f68dac3600056ba38f65894285b89d266c494b210d8f40d754f6883bf42439e5",
|
||||
"size": 1262040
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "81b0e7ee271a24ac155731b02e5d3fdc1b00e6f86afedcfb657eb3331006ad56",
|
||||
"size": 136384
|
||||
"mode": 416,
|
||||
"sha256": "60842713081ba77c08871ddf37799b26c0934615d1e3d49d22dd677be8eaf830",
|
||||
"size": 1065288
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "c4f596d81eee41ac1d11bbe74ff388c37ea88eab61d51148280cdfa2648af8a9",
|
||||
"size": 133752
|
||||
"mode": 416,
|
||||
"sha256": "1b468f0e37f7647d4b3597f945b13bcdb6261277c9ee82dde1e4db0c01ae1777",
|
||||
"size": 1065400
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "d76998ffe1a7250545dc2dd79c7f0463a16c1370000273dd6eea3acd6be7b154",
|
||||
"size": 67872
|
||||
"mode": 416,
|
||||
"sha256": "dc3f21fdd7dfc4baa6acfc884716a56de0f66974a51df1fbb8ba977b5f103ed8",
|
||||
"size": 999720
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_body.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "2c50d0a84df01de778666ea92969e28226a91b87d6da7e6ac0d419ba25743598",
|
||||
"size": 137120
|
||||
"mode": 416,
|
||||
"sha256": "bdcb85c47639922960d380b6dd2fbf68883b4fc76de48da4d9fd93668b38cb45",
|
||||
"size": 1065224
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_imu.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "673812cb76a3dc353e7726bf940d68ee8c2b9ff3316dbddce0de972e72233150",
|
||||
"size": 135816
|
||||
"mode": 416,
|
||||
"sha256": "0d69eab9b5c27d88d0c99a0cb6e9bfb9aa50310d4b823753644b3e3eb200ad3d",
|
||||
"size": 1196440
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_media.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "734054caeac7fe5b17f69ee5d2ec917a93463a562cac50d584b8b6da5a7ab4ed",
|
||||
"size": 204080
|
||||
"mode": 416,
|
||||
"sha256": "cda5e46e4746b54d25012ea5966da9778e396d74ef4bb6eac762fba4c17b6fae",
|
||||
"size": 1065224
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_policy.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "3ba641937f57ce36a3663eb85df9facb97bfb5c637a776d81b63e7e74482c2d5",
|
||||
"size": 201912
|
||||
"mode": 416,
|
||||
"sha256": "4f3666dc01af607a8860b7d26689f4790797b25633bbe5049f7a2cc3f75ea68f",
|
||||
"size": 1196472
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_upload.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "1aa10dad0406f6d71298fb017d1cb32f4516db52eb5fe97018d220171f81048e",
|
||||
"size": 205248
|
||||
"mode": 416,
|
||||
"sha256": "b836c3f257c530f56882c15ea0b836c47bc270e57eb38424cfb763a2509a1f70",
|
||||
"size": 1130696
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/session_bridge.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 416,
|
||||
"sha256": "e4805ad4be15e8ba8fd3856b87161e95e627687275b97dd68f8ac8a84ae1c38d",
|
||||
"size": 1065352
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "1fbb0bb01ce46805ca383b3ba207bf40b08647a047336fe0d00936518f241945",
|
||||
"size": 135584
|
||||
"mode": 416,
|
||||
"sha256": "4c27b4ad1ee41a5fbd854392fc5541f9532071d87a333e2fef43a253f1c78cc1",
|
||||
"size": 1065368
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "2ae7c4618618edc31b56a8720ffcf5dea7772fd6c00ce5a68703a58990757732",
|
||||
"size": 269536
|
||||
"mode": 416,
|
||||
"sha256": "badeebe1aea6c0be071bebc7bffa3368d528a48b517e5a079b40f9c8579803d7",
|
||||
"size": 1130920
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/registration/__init__.py": {
|
||||
"mode": 420,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/registration/reg_core.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 416,
|
||||
"sha256": "338b657e8e87de063ca1d04600315f4cf30ffae34be5029e6415e52d6cd4b786",
|
||||
"size": 1065304
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/telemetry/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -565,9 +516,9 @@
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/telemetry/iqtelemetryd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "be7294d1034251717952a68a6831902d5ce8ea903f69b16fc3bbf7d232043d05",
|
||||
"size": 540624
|
||||
"mode": 416,
|
||||
"sha256": "11cee4ff4e4041bbbb8ba76a0846a5eedbaee7fc172c7abba855a09e394915a1",
|
||||
"size": 1393240
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/uploaderd/__init__.py": {
|
||||
"mode": 420,
|
||||
@@ -575,9 +526,9 @@
|
||||
"size": 0
|
||||
},
|
||||
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "ea3c6f1c89a3411773fcbab54e73f5b6997765a42c5fbe6d12249eccc321d31e",
|
||||
"size": 336264
|
||||
"mode": 416,
|
||||
"sha256": "2b6d4963a7370a2cac157b3ab44c02f251d7be221fbf7ec33df0b096a0ad3e3d",
|
||||
"size": 1262072
|
||||
},
|
||||
"runtime": {
|
||||
"entries": {
|
||||
@@ -601,6 +552,14 @@
|
||||
"path": "python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so",
|
||||
"type": "python-module"
|
||||
},
|
||||
"iqpilot_private.konn3kt.hephaestus.iqsentryd": {
|
||||
"path": "python/iqpilot_private/konn3kt/hephaestus/iqsentryd.cpython-312-aarch64-linux-gnu.so",
|
||||
"type": "python-module"
|
||||
},
|
||||
"iqpilot_private.konn3kt.hephaestus.k3wgd": {
|
||||
"path": "python/iqpilot_private/konn3kt/hephaestus/k3wgd.cpython-312-aarch64-linux-gnu.so",
|
||||
"type": "python-module"
|
||||
},
|
||||
"iqpilot_private.konn3kt.hephaestus.manage_hephaestusd": {
|
||||
"path": "python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so",
|
||||
"type": "python-module"
|
||||
@@ -624,36 +583,38 @@
|
||||
}
|
||||
},
|
||||
"signatures": {
|
||||
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "YpZLvLgHCf+QVH7Yo1fml62OiJu7oUVZkvT/4VScxphS4ul8GjBe+wQ+r2pFE+E1DkZ3h+QLQoI9WgqeC0HRDg==",
|
||||
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "TVUDTKJVz5koYuF2SxNScKNChgHO3adhQdhpZtPE3IVC+zmOr/BVxGJpykyYaFx1J541DydjM3pYeiVbkBtDCA==",
|
||||
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "zFIzelMLy+b9fNs3EDCwnc5/9R4KxGQyhACAbjqGogKDuo18t1FfDnQHzoa4vYqrkLcvVTRwgVP27/CWR5sNAA==",
|
||||
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "RImusUqqBvtJLKEgQbzOLZRn8izo9Pzll7BaFbqqfeJCaqRGBcWzNc70YcJQ5wodEaQzaY2DLIsu07l/o0ScAQ==",
|
||||
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "MCSdZIEfeTNKhHrL/vQChDUxRx51miixM0UAMJJ4SKkvbvmVwN56Y34B6A/aLRJvVDH8YXyUZnUCQaR8+UhgCg==",
|
||||
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": "YP/Op7Ol87glLrYv77mMifZyBM6d4Dj/X3wiNKIcB8ECaDCtbhzTmOwlh2eO+HZIWdxT57PCu1FEhdWlb7RaCA==",
|
||||
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": "uNYw3cE1Ffda+71eLKrU3sjFrpG7xNuhfTNf8X8ubGYFhp3tmAf2sp1+hTyvef/Mi+QjtF00CE0rnZ/dra98CQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "xibUheiHQQs9Cx7ygWhYYjr7/CGBMvsSv1E0IhLyejZifAnUQQ1tK0wWcfdwFrzw3iFTgO9hp55fDHXPJV2qDw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_connection_parameters.cpython-312-aarch64-linux-gnu.so": "WguojStOM6IoIAZ3IFCmUcIJowx8UtuL8+qgn61BzvfnF2/AVS/9a+vdVvKZXwXMuZsAkKAd+GZWt4rLPUFXBw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "NqiUDgwj60ZdB6SYVIPkGh0HwFIQv2z0p0BKKingy5EyFThcKfbELMk86wYRbXAZnjkCUdTWrhxsxp9sIrSEAw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "+xL+bgAX904lJa3+fesZC3e0FAf+CbXByelJpRIjlz94eGG3h5qVgozeFalGUZYdhZpJb42SyAze4DcpaxPUAg==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "kmmmMdiMMIIslmInzMjniLfmp/VP5scfuAbRAvNA9QRbmxxw4ko5nnya0VhTjS7d8hM0rRZ/McI4qAJByPpYCw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "LLGPCnUzDdyF22dJ/qhI6gYPU8hx3xeL9LeyOEqP//gzoa4jNCJeI+HWYLz0teOSoJssiGwLJ+ZuAtuT7MIpCQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "kHEBEwEVZAodATQ/+/wSNyKJ5S7p2AiTqZ+kfPAmmb3ihr4xVyuy94fAkV87WIzaO1SzInB8x6SGg6UghsZiBQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/driving_settings.cpython-312-aarch64-linux-gnu.so": "03FhZYwHUlbfbOxYDX8CwC2gIwl6KDGRaYmCGdIx6gcg4IdtAdVyrgIw7OWDIqKuHXqlckDnf+Jh3KOClpTOAw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "v6huZjXXSNqSNprbiFaUgC30HDcoIiyEC7A105rut+dM3KCBisvYe/adu4p4PQVS8T2qNbqnHGboZd2NHtKQCA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/iqsentryd.cpython-312-aarch64-linux-gnu.so": "aFGz1KAWpY4O/+4xXZ+/Yfq9GLQ7IEZqNNQjEtRVjxDE7XEukXK3s4dXWpeZ9pg5j1NhmcCn/bnGaR+p9JWUBQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/iqsentryvisiond.cpython-312-aarch64-linux-gnu.so": "gmK55AoOUqaRL4qlpQFtSYGrPkaZ+2VtN+0XqHhwY8NH8gXZUEevFhNi/yne5qdw9TPW7k1xM0kE43cNpKAEBA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/k3wgd.cpython-312-aarch64-linux-gnu.so": "+0CBcRpRn/sXdSxnPRIV8Hv/HO0/3+dsoIbkGePdunOxWujxmPbaFYh6jADa8vrhh4AL+UDaaJQGfMjy/3CzCg==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "8CbpnBxEs/4IEKRupI+K2b//RlL8wGmmR99RUIa40XtOrq0Y2oZkk4qQWqWx0Leth3tj2euz3eSdm0OwrYerAg==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "+b6HQ5D++9rppi1E1mHVQjaMXy0tB0KLb036r4sX8JslK3pXuiP1HYP4kqpugNEBhUTvI5shcsYndKi0PBRvDA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "1qSKc1Ge2UVIevFR4gCR+f8N3zlRoK+kRU+Db56lfzEB0f7S72mrMQpE3QD0ZRfQ+IngyYj71TueRAYTuUOZCA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_body.cpython-312-aarch64-linux-gnu.so": "FMnBSL3lt83BfmjqfHGGdGl3Ivq4D5GwCXoULBsiq4uEKBO7v2ADru0poTJzjRCrPTTv8nnteWT+lGffEXO4DQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_imu.cpython-312-aarch64-linux-gnu.so": "Qp3+AE9b6G1bw6IhSGAPY9NZJeqqzsVf0vLVhdQ+CWkIesNIBAWTNixW9xLZ6bxVnw4Jvk4k5n404iaOP2iPDw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_media.cpython-312-aarch64-linux-gnu.so": "yK5QgYja6wGUAEDAjvnhZr7kvS72pekXRGKuDfKPFUSehFVJVx+HNwWvKNLZDE60UNyeNzCf0XG40zxsN+ufDQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_policy.cpython-312-aarch64-linux-gnu.so": "DvORZ1fUs4uW/eRe6RiN8jp2tUJU2Mnn/y9M9XbNSXieKKWFsPgyvYAQS4TvhZc2+S1+cNbgoqqihu8EcFu7BA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_upload.cpython-312-aarch64-linux-gnu.so": "Hu5AB59owBNkFqKmvfshrOXBSDa0I02ydUifnHjiq+7tocZNrOmjnsBPbcQgSuHYPgaux5W1r0fBmwpGLs9PAA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "CEXfY5TOPY7JyHubxBeyW+WS0mrCU6nH3HB2hglnA/xdIWrRxNo0DzkegS1LRDhNCvwqThMLX3c6DY8yR8urCw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "Wo9GEpl8kt2tPyy/7lX9d7a2gMI0gL1XrLKtTIkhJbq78qqp/TtzZOuXty2y9NvGnoXmJdRSCRfOMBFZkY/mAA==",
|
||||
"python/iqpilot_private/konn3kt/telemetry/iqtelemetryd.cpython-312-aarch64-linux-gnu.so": "gYuyE8q9g3GsKXMfMoXrLCYZvz2XJDicuriUutLPgz1KBKEZDgsAVNUxfR5oztOV+OSAb11by5nb9gi9vo62BA==",
|
||||
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "ZCpdDtvv/E9G2tkoFT027BhNaA6ZBtlwU9m7lyKiY3nXiD0hk5m2mkVnbwKXolNXBCyCiQa3YmL5fKmPDqUqBg=="
|
||||
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "0qFnstW83THKbKdU/6x895gXoJLMF5kp9PhxhSoja0t/D/kQmp1QI0u7zhi3idsk8pwYzZCN8YMIRBrVALFsCg==",
|
||||
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "eFqA9ShbE8zeqXxEaEqV0KOXKbCP/9SQifNXvRR3Y+of29oxKT+CEK5OyOdNnEjaEG+u+gTXMOd3oSsk/L+wAQ==",
|
||||
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "Qx5fS+scNnelKqICvs1iRFY8httSCmDCRQLpB95hK4Riakydm5pwLTJF1YUR0Oz0cf4it+INwKcoUmVkAG9kBw==",
|
||||
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "R9j9s8waY7kThuL5491x9r7C/9ROkVZWigxKaThgKC8s0Je9xIb/EFuO58reyJUVx6P37rXVU2XxSm+MvZdLCg==",
|
||||
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "UkT0VaaRxweoTRjfzP3E38fwVRgEI1Fj/HMl6HPxuiBrOJhOSVWlJHwHKtYgFrzVBhUwNqnHrmAbdLy6ZN9LBg==",
|
||||
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": "2AnnmUawhW1vWt76WabuiXHM779pEE8hC5xHVEdZ7cHN34RJDg+Q1ZyO/5arv2ntek2Vp5v3F8dlRm6FpfQSBA==",
|
||||
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": "Aq7CLbLUNrZlTq6XWlexzlKS/31+9H+XRKzZRI7VVBpiD9VEtlcB7u49RRdcEH5gfpGZFLDIMrfS1J01yoggAg==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "gXWjTUUp8r9dDyUMaXBpEUBZCTFH70Gwb8SEj+rWFV69iB1wOijDeW9P7danSn8iw3y68l6AcVV7u3y8F+trDg==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_connection_parameters.cpython-312-aarch64-linux-gnu.so": "HqaR/WsSKt1UgAFo9cg4plqPGK7719drj6AE6GwZriK6vrLZrC2KZRwNTul7RT39RsatksN3J4oNw85I5LCPAw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "ygAT4jof7Gu4vS2X+8GMic0D2QCTXCAUdU9waYi8vlyi+6hmrgbWMM/BDjDdIJoYMxjnn3zRYdN0Z6BD9PrcBg==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "1yQX0yOs4u8K08p5nlyQT+1UwpH96PgMTYZlpCvNwbC8o5xex4lQJyI6lirgpHXfYltVmEgSgw8N1i0GA7yGCQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "s83uFeK7C19GQKrq5R+L3poMHRo3VCj4NMq46gWrC+ofOoRmPRgkaGSqGrDGr8m/EIMbj4bpYInkxqbjqy3AAQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "ETryY+pFucqOyF5iTwE4g6mnf/luxyM3VljIioWLLppDpf8WN9WVv2Qj0r4GVsAg7iwf37HOW/dvj5EtfEiPBA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "HK2d7XJdB/NScUdK0kcpOOEwlehCjXKDiz0t3qul7TSGTw/TP7fQRfottuWhynWk3WseDpDruerOM9XZNJQzDw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/driving_settings.cpython-312-aarch64-linux-gnu.so": "u8BScZvFva8KW03yPS1XI1VwGjLGcjMy0UMLL7X9GgksKdBKEJ/DQLU9wx3FunBk8yRQ8L/K5mq3TTGUpLn+BQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "6V6EGUYF2qEA6tfQeUs9oTMLDuAOkx1OpDrS3LJNNHuyw+QbK33iYGAiH07eNs2GjL8psL3zS1tbn2x4p6udAw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/iqsentryd.cpython-312-aarch64-linux-gnu.so": "QUrRj3RbxO8WaTZGn5AZBTAGQYrviI8Q9IKlX7DRyVd+kq1GwAgftTS6tDLimQNHHhLXj1ZN/wnxVqKoBrGrAg==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/iqsentryvisiond.cpython-312-aarch64-linux-gnu.so": "e0UybV5I5fAtvmF2L+1059qtu9dXCnkWUb+kdOpnUl+YXdWRwOL7TdR9TSitdNfGFRmxPfh5DwdNSToe2ZlwAw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/k3wgd.cpython-312-aarch64-linux-gnu.so": "GKxP/VjKIdlFW7EIDu/83MOOjKhcgjk7GqsIJzuEwlsCIWlir9cOjbJB/pgwE/mnW+BPZwKMzYeeVdUXCK+rDw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "7TUFhgkXVyx4JT5aFzlK+DIm7WxF4taRsSVabHHITFbon7XXBv7YjhTsaBTZL4d83eSfj4B7qU+1oHzFclTOBQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "9CBMXV4OHwtRfxXpoXvq6xJ0rb4jE54xMZUEJz47JjOBctahb3xBKpo02pNbVKnZqZEiNmPKIDqAoHCkwtbODA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "2mJaygzUnf3XXy8z3/yrFc6OtZf8MDnRE2bn3gXx3EFo/otVuBHjyVu1Mv0H8SymIRN7I/Fn+ao+lpM7ILTWCQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_body.cpython-312-aarch64-linux-gnu.so": "ZeAj6MuMrsp+mlzimai1v31Jalb7yuClrRSsDIL2Hyh76efNxajwPOx5evu0Zk1jVznu3GLtXIE8qFwWqrXXBQ==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_imu.cpython-312-aarch64-linux-gnu.so": "EJiZdjHFhyFThk1YZAYRa28cjbEa0jkSDmjNoMonT2yGyMVaxfkdU5OI86GR+LY+kEFxhRPsIlO8YSAp7PpqDA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_media.cpython-312-aarch64-linux-gnu.so": "IaXakjeDlvwMDS7NUG0f6E/3WTqv8FwgryUtRuXYXP67bmQfrzBI/kT8e+7FH4i49fu+CyrGbS42+JhsXEpwAA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_policy.cpython-312-aarch64-linux-gnu.so": "30KviVv+3hNyDLKPF+IvFbWoSH7JGgL8MKQLLR7Rrndxff6xsIw9fjFrjJuWA8q/RAkO/zw/ptJOCBFLGgTQDw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/sentry_upload.cpython-312-aarch64-linux-gnu.so": "Qw7U90kMvbmWcIaQl4AwLY/NesGZmFEDT5DpRw2t+IC//yr1aZqH5NteA8oEcLnnVxlqmVkP1DMu/5Dn4aBiDA==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/session_bridge.cpython-312-aarch64-linux-gnu.so": "8xFCNu6XKY52fe6xOxPM0jDOC1fkn6G/dDVct9t/WsnScSLBb7tOaPJKl+G3zk4xlttrztdK9haPoDOFlApuBw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "xBhqIugWTyGqS1Qd8hXTcL2BX4dwY5EhO2taJEBgwKhU/o17zv7+7GufJiIsAHambrVWI06fnmUA9mZXEBSRDw==",
|
||||
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "cHJU8JNjRlQ9mdxUsW7VU8hr7yFohd5S2xRjIF8n4mLtfA+IiwTBPWj58HOHK9QbMG2O5uyiQnGd+pINcaF2Cg==",
|
||||
"python/iqpilot_private/konn3kt/registration/reg_core.cpython-312-aarch64-linux-gnu.so": "Pbz/pYFEjYy2Ywx8HLxYfSZt447S+X7of2ADDrPDjWR4qwdy3mUSUgmOR20jm0SeDlhe9GtI3kbc5I3F6GrdBw==",
|
||||
"python/iqpilot_private/konn3kt/telemetry/iqtelemetryd.cpython-312-aarch64-linux-gnu.so": "hMIg0GAGId13kARPXKz3kUDi20oF707izEkG2CWymliKdG+1L/8AYn6ib4whIl9NBszjy7vnmAzznbjTbmfgBA==",
|
||||
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "Vj5Encl2ybZctTrlmZ9f5puYEBIm6ESgscF9Ee414GN88pyQnsUUhqzCeN6IaAJ9T/2tt7n/gEKSyptHCVOUCg=="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
+bka3mSd+pxAt1QcXidY5AAFJXqrazscCuWg0zNSJmjsEk7/LaSXKaHcTCOpBOLN/oMF5nQCsmHnoJUwOEVsDQ==
|
||||
FdPsL8Lon52Th92plPCF4TsZpz+7qfhZ7yFhrfKKVDknEnF3n+t9LchpGq3n4KuCnhfCO+dszLtoNQfSq/qkDA==
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
[
|
||||
"iqpilot.common.api",
|
||||
"iqpilot.common.api.base",
|
||||
"iqpilot.common.api.comma_connect",
|
||||
"iqpilot.common.atlas_alerts",
|
||||
"iqpilot.common.api.konn3kt",
|
||||
"iqpilot.common.basedir",
|
||||
"iqpilot.common.constants",
|
||||
"iqpilot.common.file_helpers",
|
||||
"iqpilot.common.git_creds",
|
||||
"iqpilot.common.gpio",
|
||||
"iqpilot.common.i2c",
|
||||
"iqpilot.common.issue_debug",
|
||||
"iqpilot.common.logging_extra",
|
||||
"iqpilot.common.realtime",
|
||||
"iqpilot.common.spinner",
|
||||
"iqpilot.common.swaglog",
|
||||
"iqpilot.common.time_helpers",
|
||||
"iqpilot.common.utils",
|
||||
@@ -28,10 +24,6 @@
|
||||
"iqpilot.selfdrive.iqmodeld.model_bundle_downloader",
|
||||
"iqpilot.selfdrive.iqmodeld.models.fetcher",
|
||||
"iqpilot.selfdrive.iqmodeld.models.helpers",
|
||||
"iqpilot.selfdrive.locationd.calibrationd",
|
||||
"iqpilot.selfdrive.selfdrived.alertmanager",
|
||||
"iqpilot.selfdrive.selfdrived.events",
|
||||
"iqpilot.selfdrive.ui.feedback.feedbackd",
|
||||
"iqpilot.system.hardware",
|
||||
"iqpilot.system.hardware.base",
|
||||
"iqpilot.system.hardware.hw",
|
||||
@@ -47,7 +39,6 @@
|
||||
"iqpilot.system.loggerd.xattr_cache",
|
||||
"iqpilot.system.manager.process",
|
||||
"iqpilot.system.manager.process_config",
|
||||
"iqpilot.system.micd",
|
||||
"iqpilot.system.phonegps.bridge",
|
||||
"iqpilot.system.sentry",
|
||||
"iqpilot.system.ui.lib.networkmanager",
|
||||
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
import iqpilot.common.api.comma_connect
|
||||
import iqpilot.common.api.konn3kt
|
||||
|
||||
|
||||
class Api:
|
||||
def __init__(self, dongle_id):
|
||||
self.service = iqpilot.common.api.comma_connect.CommaConnectApi(dongle_id)
|
||||
self.service = iqpilot.common.api.konn3kt.Konn3ktApi(dongle_id)
|
||||
|
||||
def request(self, method, endpoint, **params):
|
||||
return self.service.request(method, endpoint, **params)
|
||||
@@ -19,8 +19,8 @@ class Api:
|
||||
|
||||
|
||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, session=None, **params):
|
||||
return iqpilot.common.api.comma_connect.CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
|
||||
return iqpilot.common.api.konn3kt.Konn3ktApi(None).api_get(endpoint, method, timeout, access_token, session, **params)
|
||||
|
||||
|
||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||
return iqpilot.common.api.comma_connect.CommaConnectApi(None).get_key_pair()
|
||||
return iqpilot.common.api.konn3kt.Konn3ktApi(None).get_key_pair()
|
||||
|
||||
@@ -7,10 +7,44 @@ from functools import lru_cache
|
||||
from iqpilot.system.hardware.hw import Paths
|
||||
from iqpilot.system.version import get_version
|
||||
|
||||
# name: jwt signature algorithm
|
||||
KEYS = {"id_rsa": "RS256",
|
||||
"id_ecdsa": "ES256"}
|
||||
|
||||
_CUSTODIAN_KEY = object()
|
||||
_IQKEEPER_SOCKET = "/run/iqkeeper/iqkeeper.sock"
|
||||
|
||||
|
||||
def _custodian_available() -> bool:
|
||||
import stat
|
||||
try:
|
||||
return stat.S_ISSOCK(os.stat(_IQKEEPER_SOCKET).st_mode)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def _custodian_sign_jwt(dongle_id: str, payload: dict, alg: str) -> str:
|
||||
import base64
|
||||
import json
|
||||
import socket
|
||||
nonce = base64.b64encode(os.urandom(16)).decode()
|
||||
packet = json.dumps({
|
||||
"v": 1,
|
||||
"op": "sign",
|
||||
"dongle_id": dongle_id,
|
||||
"payload": payload,
|
||||
"alg": alg,
|
||||
"nonce": nonce,
|
||||
}).encode()
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) as sock:
|
||||
sock.settimeout(25.0)
|
||||
sock.connect(_IQKEEPER_SOCKET)
|
||||
sock.send(packet)
|
||||
reply_data = sock.recv(8192)
|
||||
reply = json.loads(reply_data) if reply_data else {}
|
||||
if reply.get("nonce") != nonce or not reply.get("ok"):
|
||||
raise PermissionError(f"custodian sign failed: {reply.get('error', 'unknown')}")
|
||||
return reply["token"]
|
||||
|
||||
|
||||
@lru_cache(maxsize=4)
|
||||
def load_signing_key(private_key: str):
|
||||
@@ -60,6 +94,9 @@ class BaseApi:
|
||||
}
|
||||
if payload_extra is not None:
|
||||
payload.update(payload_extra)
|
||||
if self.private_key is _CUSTODIAN_KEY:
|
||||
serializable = {k: v.isoformat() if isinstance(v, datetime) else v for k, v in payload.items()}
|
||||
return _custodian_sign_jwt(self.dongle_id, serializable, self.jwt_algorithm)
|
||||
key = load_signing_key(self.private_key) if self.private_key else self.private_key
|
||||
token = jwt.encode(payload, key, algorithm=self.jwt_algorithm)
|
||||
if isinstance(token, bytes):
|
||||
@@ -73,9 +110,25 @@ class BaseApi:
|
||||
return self.request(method, endpoint, timeout, access_token, session=session, json=json, **params)
|
||||
|
||||
@staticmethod
|
||||
def get_key_pair() -> tuple[str, str, str] | tuple[None, None, None]:
|
||||
def get_key_pair() -> tuple[str, str | object, str] | tuple[None, None, None]:
|
||||
for key in KEYS:
|
||||
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
|
||||
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
|
||||
return KEYS[key], private.read(), public.read()
|
||||
priv_path = Paths.persist_root() + f'/comma/{key}'
|
||||
pub_path = priv_path + '.pub'
|
||||
if not os.path.isfile(priv_path) or not os.path.isfile(pub_path):
|
||||
continue
|
||||
try:
|
||||
with open(pub_path) as public:
|
||||
pub = public.read()
|
||||
except OSError:
|
||||
continue
|
||||
try:
|
||||
with open(priv_path) as private:
|
||||
return KEYS[key], private.read(), pub
|
||||
except PermissionError:
|
||||
import time
|
||||
for _attempt in range(10):
|
||||
if _custodian_available():
|
||||
return KEYS[key], _CUSTODIAN_KEY, pub
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
return None, None, None
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) 2026 IQ.Lvbs. All rights reserved.
|
||||
import os
|
||||
|
||||
from iqpilot.common.api.base import BaseApi
|
||||
@@ -5,7 +6,7 @@ from iqpilot.common.api.base import BaseApi
|
||||
API_HOST = os.getenv('API_HOST', 'https://api-iqlabs.konn3kt.com')
|
||||
|
||||
|
||||
class CommaConnectApi(BaseApi):
|
||||
class Konn3ktApi(BaseApi):
|
||||
def __init__(self, dongle_id):
|
||||
super().__init__(dongle_id, API_HOST)
|
||||
self.user_agent = "openpilot-"
|
||||
-277
@@ -1,277 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from bisect import insort
|
||||
from collections.abc import Callable, Iterable
|
||||
from dataclasses import dataclass, field
|
||||
from enum import IntEnum
|
||||
|
||||
import iqpilot.cereal.messaging as messaging
|
||||
from iqpilot.cereal import car, log
|
||||
from iqpilot.common.realtime import DT_CTRL
|
||||
from iqpilot.system.hardware import HARDWARE
|
||||
|
||||
AlertSize = log.SelfdriveState.AlertSize
|
||||
AlertStatus = log.SelfdriveState.AlertStatus
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
|
||||
|
||||
|
||||
def _frames_for(seconds: float) -> int:
|
||||
return int(seconds / DT_CTRL)
|
||||
|
||||
|
||||
class Tier(IntEnum):
|
||||
LOWEST = 0
|
||||
LOWER = 1
|
||||
LOW = 2
|
||||
MID = 3
|
||||
HIGH = 4
|
||||
HIGHEST = 5
|
||||
|
||||
|
||||
class Tags:
|
||||
ENABLE = "enable"
|
||||
PRE_ENABLE = "preEnable"
|
||||
OVERRIDE_LATERAL = "overrideLateral"
|
||||
OVERRIDE_LONGITUDINAL = "overrideLongitudinal"
|
||||
NO_ENTRY = "noEntry"
|
||||
WARNING = "warning"
|
||||
USER_DISABLE = "userDisable"
|
||||
SOFT_DISABLE = "softDisable"
|
||||
IMMEDIATE_DISABLE = "immediateDisable"
|
||||
PERMANENT = "permanent"
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class AlertCard:
|
||||
alert_text_1: str
|
||||
alert_text_2: str
|
||||
alert_status: log.SelfdriveState.AlertStatus
|
||||
alert_size: log.SelfdriveState.AlertSize
|
||||
priority: Tier
|
||||
visual_alert: car.CarControl.HUDControl.VisualAlert
|
||||
audible_alert: car.CarControl.HUDControl.AudibleAlert
|
||||
duration: int
|
||||
creation_delay: float = 0.0
|
||||
alert_type: str = field(default="", init=False)
|
||||
event_type: str | None = field(default=None, init=False)
|
||||
|
||||
def __init__(self,
|
||||
alert_text_1: str,
|
||||
alert_text_2: str,
|
||||
alert_status: log.SelfdriveState.AlertStatus,
|
||||
alert_size: log.SelfdriveState.AlertSize,
|
||||
priority: Tier,
|
||||
visual_alert: car.CarControl.HUDControl.VisualAlert,
|
||||
audible_alert: car.CarControl.HUDControl.AudibleAlert,
|
||||
duration: float,
|
||||
creation_delay: float = 0.0):
|
||||
self.alert_text_1 = alert_text_1
|
||||
self.alert_text_2 = alert_text_2
|
||||
self.alert_status = alert_status
|
||||
self.alert_size = alert_size
|
||||
self.priority = priority
|
||||
self.visual_alert = visual_alert
|
||||
self.audible_alert = audible_alert
|
||||
self.duration = _frames_for(duration)
|
||||
self.creation_delay = creation_delay
|
||||
self.alert_type = ""
|
||||
self.event_type = None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.alert_text_1}/{self.alert_text_2} {self.priority} {self.visual_alert} {self.audible_alert}"
|
||||
|
||||
|
||||
AlertFactory = Callable[[car.CarParams, car.CarState, messaging.SubMaster, bool, int, log.ControlsState], AlertCard]
|
||||
|
||||
|
||||
def car_mode_entry_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> AlertCard:
|
||||
del CS, sm, metric, soft_disable_time, personality
|
||||
headline = "Enable Adaptive Cruise to Engage"
|
||||
if CP.brand == "honda":
|
||||
headline = "Enable Main Switch to Engage"
|
||||
return NoEntryCard(headline)
|
||||
|
||||
|
||||
class EventBook(ABC):
|
||||
def __init__(self):
|
||||
self._live_names: list[int] = []
|
||||
self._latched_names: list[int] = []
|
||||
self.event_counters: dict[int, int] = {}
|
||||
|
||||
@property
|
||||
def events(self) -> list[int]:
|
||||
return self._live_names
|
||||
|
||||
@events.setter
|
||||
def events(self, values: list[int]) -> None:
|
||||
self._live_names = values
|
||||
|
||||
@property
|
||||
def static_events(self) -> list[int]:
|
||||
return self._latched_names
|
||||
|
||||
@static_events.setter
|
||||
def static_events(self, values: list[int]) -> None:
|
||||
self._latched_names = values
|
||||
|
||||
@property
|
||||
def names(self) -> list[int]:
|
||||
return list(self._live_names)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._live_names)
|
||||
|
||||
def add(self, event_name: int, static: bool = False) -> None:
|
||||
if static:
|
||||
insort(self._latched_names, event_name)
|
||||
insort(self._live_names, event_name)
|
||||
|
||||
def clear(self) -> None:
|
||||
refreshed: dict[int, int] = {}
|
||||
for event_name, frames_seen in self.event_counters.items():
|
||||
refreshed[event_name] = frames_seen + 1 if event_name in self._live_names else 0
|
||||
self.event_counters = refreshed
|
||||
self._live_names = list(self._latched_names)
|
||||
|
||||
def contains(self, event_type: str) -> bool:
|
||||
board = self.get_events_mapping()
|
||||
return any(event_type in board.get(event_name, {}) for event_name in self._live_names)
|
||||
|
||||
def has(self, event_name: int) -> bool:
|
||||
return event_name in self._live_names
|
||||
|
||||
def contains_in_list(self, events_list: list[int]) -> bool:
|
||||
return any(event_name in self._live_names for event_name in events_list)
|
||||
|
||||
def remove(self, event_name: int, static: bool = False) -> None:
|
||||
if static and event_name in self._latched_names:
|
||||
self._latched_names.remove(event_name)
|
||||
|
||||
if event_name in self._live_names:
|
||||
self.event_counters[event_name] = self.event_counters.get(event_name, 0) + 1
|
||||
self._live_names.remove(event_name)
|
||||
|
||||
def add_from_msg(self, events: Iterable) -> None:
|
||||
for event in events:
|
||||
insort(self._live_names, event.name.raw)
|
||||
|
||||
def to_msg(self):
|
||||
board = self.get_events_mapping()
|
||||
outbound = []
|
||||
for event_name in self._live_names:
|
||||
msg = self.get_event_msg_type().new_message()
|
||||
msg.name = event_name
|
||||
for event_kind in board.get(event_name, {}):
|
||||
setattr(msg, event_kind, True)
|
||||
outbound.append(msg)
|
||||
return outbound
|
||||
|
||||
def create_alerts(self, event_types: list[str], callback_args=None):
|
||||
callback_args = [] if callback_args is None else callback_args
|
||||
board = self.get_events_mapping()
|
||||
spawned: list[AlertCard] = []
|
||||
for event_name in self._live_names:
|
||||
variants = board.get(event_name, {})
|
||||
for event_type in event_types:
|
||||
chosen = variants.get(event_type)
|
||||
if chosen is None:
|
||||
continue
|
||||
alert = self._realize(chosen, callback_args)
|
||||
age_frames = self.event_counters.get(event_name, 0) + 1
|
||||
if age_frames * DT_CTRL < alert.creation_delay:
|
||||
continue
|
||||
alert.alert_type = f"{self.get_event_name(event_name)}/{event_type}"
|
||||
alert.event_type = event_type
|
||||
spawned.append(alert)
|
||||
return spawned
|
||||
|
||||
@staticmethod
|
||||
def _realize(candidate: AlertCard | AlertFactory, callback_args: list) -> AlertCard:
|
||||
return candidate if isinstance(candidate, AlertCard) else candidate(*callback_args)
|
||||
|
||||
@abstractmethod
|
||||
def get_events_mapping(self) -> dict[int, dict[str, AlertCard | AlertFactory]]:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def get_event_name(self, event: int) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def get_event_msg_type(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def _mici_reframe(primary: str, secondary: str) -> tuple[str, str, log.SelfdriveState.AlertSize]:
|
||||
if HARDWARE.get_device_type() == "mici":
|
||||
return secondary, primary, AlertSize.small
|
||||
return primary, secondary, AlertSize.mid
|
||||
|
||||
|
||||
class NoEntryCard(AlertCard):
|
||||
def __init__(self,
|
||||
alert_text_2: str,
|
||||
alert_text_1: str = "IQ.Pilot Unavailable",
|
||||
visual_alert: car.CarControl.HUDControl.VisualAlert = VisualAlert.none,
|
||||
priority: Tier = Tier.LOW):
|
||||
primary, secondary, size = _mici_reframe(alert_text_1, alert_text_2)
|
||||
super().__init__(primary, secondary, AlertStatus.normal, size, priority, visual_alert, AudibleAlert.refuse, 3.0)
|
||||
|
||||
|
||||
class GentleDisableCard(AlertCard):
|
||||
def __init__(self, alert_text_2: str):
|
||||
super().__init__(
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
alert_text_2,
|
||||
AlertStatus.userPrompt,
|
||||
AlertSize.full,
|
||||
Tier.MID,
|
||||
VisualAlert.steerRequired,
|
||||
AudibleAlert.warningSoft,
|
||||
2.0,
|
||||
)
|
||||
|
||||
|
||||
class PendingDisableCard(GentleDisableCard):
|
||||
def __init__(self, alert_text_2: str):
|
||||
super().__init__(alert_text_2)
|
||||
self.alert_text_1 = "IQ.Pilot will disengage"
|
||||
|
||||
|
||||
class HardDisableCard(AlertCard):
|
||||
def __init__(self, alert_text_2: str):
|
||||
super().__init__(
|
||||
"TAKE CONTROL IMMEDIATELY",
|
||||
alert_text_2,
|
||||
AlertStatus.critical,
|
||||
AlertSize.full,
|
||||
Tier.HIGHEST,
|
||||
VisualAlert.steerRequired,
|
||||
AudibleAlert.warningImmediate,
|
||||
4.0,
|
||||
)
|
||||
|
||||
|
||||
class ChimeCard(AlertCard):
|
||||
def __init__(self, audible_alert: car.CarControl.HUDControl.AudibleAlert):
|
||||
super().__init__("", "", AlertStatus.normal, AlertSize.none, Tier.MID, VisualAlert.none, audible_alert, 0.2)
|
||||
|
||||
|
||||
class BannerCard(AlertCard):
|
||||
def __init__(self, alert_text_1: str, alert_text_2: str = "", duration: float = 0.2, priority: Tier = Tier.LOWER, creation_delay: float = 0.0):
|
||||
size = AlertSize.mid if alert_text_2 else AlertSize.small
|
||||
super().__init__(alert_text_1, alert_text_2, AlertStatus.normal, size, priority, VisualAlert.none, AudibleAlert.none, duration, creation_delay)
|
||||
|
||||
|
||||
class BootCard(AlertCard):
|
||||
def __init__(self, alert_text_1: str, alert_text_2: str = "Always keep hands on wheel and eyes on road", alert_status=AlertStatus.normal):
|
||||
if HARDWARE.get_device_type() == "mici":
|
||||
compact_secondary = "" if alert_text_2 == "Always keep hands on wheel and eyes on road" else alert_text_2
|
||||
super().__init__(alert_text_1, compact_secondary, alert_status, AlertSize.small, Tier.LOWER, VisualAlert.none, AudibleAlert.none, 5.0)
|
||||
else:
|
||||
super().__init__(alert_text_1, alert_text_2, alert_status, AlertSize.mid, Tier.LOWER, VisualAlert.none, AudibleAlert.none, 5.0)
|
||||
|
||||
|
||||
NULL_ALERT = AlertCard("", "", AlertStatus.normal, AlertSize.none, Tier.LOWEST, VisualAlert.none, AudibleAlert.none, 0.0)
|
||||
@@ -1,23 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
# conversions
|
||||
class CV:
|
||||
# Speed
|
||||
MPH_TO_KPH = 1.609344
|
||||
KPH_TO_MPH = 1. / MPH_TO_KPH
|
||||
MS_TO_KPH = 3.6
|
||||
KPH_TO_MS = 1. / MS_TO_KPH
|
||||
MS_TO_MPH = MS_TO_KPH * KPH_TO_MPH
|
||||
MPH_TO_MS = MPH_TO_KPH * KPH_TO_MS
|
||||
MS_TO_KNOTS = 1.9438
|
||||
KNOTS_TO_MS = 1. / MS_TO_KNOTS
|
||||
|
||||
# Angle
|
||||
DEG_TO_RAD = np.pi / 180.
|
||||
RAD_TO_DEG = 1. / DEG_TO_RAD
|
||||
|
||||
# Mass
|
||||
LB_TO_KG = 0.453592
|
||||
|
||||
|
||||
ACCELERATION_DUE_TO_GRAVITY = 9.81 # m/s^2
|
||||
@@ -1,36 +0,0 @@
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from iqpilot.system.hardware import PC
|
||||
from iqpilot.system.hardware.hw import Paths
|
||||
|
||||
|
||||
DEBUG_FILENAME = "iqpilot_issue_debug.txt"
|
||||
DEBUG_PATH = Path(Paths.comma_home()) / "community" / DEBUG_FILENAME if PC else Path("/data/community") / DEBUG_FILENAME
|
||||
|
||||
_lock = threading.Lock()
|
||||
_last_log_times: dict[str, float] = {}
|
||||
|
||||
|
||||
def log_issue(tag: str, message: str) -> None:
|
||||
try:
|
||||
DEBUG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
with _lock:
|
||||
with open(DEBUG_PATH, "a", encoding="utf-8") as f:
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
f.write(f"[{timestamp}] [{tag}] {message}\n")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def log_issue_limited(key: str, tag: str, message: str, interval_sec: float = 1.0) -> None:
|
||||
now = time.monotonic()
|
||||
with _lock:
|
||||
last = _last_log_times.get(key, 0.0)
|
||||
if now - last < interval_sec:
|
||||
return
|
||||
_last_log_times[key] = now
|
||||
|
||||
log_issue(tag, message)
|
||||
@@ -1,52 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
from iqpilot.common.basedir import BASEDIR
|
||||
|
||||
|
||||
class Spinner:
|
||||
def __init__(self):
|
||||
try:
|
||||
self.spinner_proc = subprocess.Popen(["./spinner.py"],
|
||||
stdin=subprocess.PIPE,
|
||||
cwd=os.path.join(BASEDIR, "iqpilot", "system", "ui"),
|
||||
close_fds=True)
|
||||
except OSError:
|
||||
self.spinner_proc = None
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def update(self, spinner_text: str):
|
||||
if self.spinner_proc is not None:
|
||||
self.spinner_proc.stdin.write(spinner_text.encode('utf8') + b"\n")
|
||||
try:
|
||||
self.spinner_proc.stdin.flush()
|
||||
except BrokenPipeError:
|
||||
pass
|
||||
|
||||
def update_progress(self, cur: float, total: float):
|
||||
self.update(str(round(100 * cur / total)))
|
||||
|
||||
def close(self):
|
||||
if self.spinner_proc is not None:
|
||||
self.spinner_proc.kill()
|
||||
try:
|
||||
self.spinner_proc.communicate(timeout=2.)
|
||||
except subprocess.TimeoutExpired:
|
||||
print("WARNING: failed to kill spinner")
|
||||
self.spinner_proc = None
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import time
|
||||
with Spinner() as s:
|
||||
s.update("Spinner text")
|
||||
time.sleep(5.0)
|
||||
print("gone")
|
||||
time.sleep(5.0)
|
||||
@@ -31,24 +31,44 @@ class Timer:
|
||||
realtime = f"{duration/total:.1f}x realtime" if total > 0 else "N/A"
|
||||
return f"{duration}s in {total:.1f}s ({realtime}) | {parts}"
|
||||
|
||||
_sudo_available: bool | None = None
|
||||
|
||||
|
||||
def sudo_write(val: str, path: str) -> None:
|
||||
global _sudo_available
|
||||
try:
|
||||
with open(path, 'w') as f:
|
||||
f.write(str(val))
|
||||
except PermissionError:
|
||||
os.system(f"sudo chmod a+w {path}")
|
||||
try:
|
||||
return
|
||||
except OSError:
|
||||
pass
|
||||
# AGNOS 4.9.9.2 removes owner root; a privileged boot step opens these nodes to the stack so a direct write normally works. Only probe a non-interactive escalation once (interactive sudo blocks with no tty, and sudo -n itself can stall), and never let its failure or timeout propagate.
|
||||
if _sudo_available is False:
|
||||
return
|
||||
try:
|
||||
_sudo_available = subprocess.run(["sudo", "-n", "chmod", "a+w", path], timeout=2, capture_output=True).returncode == 0
|
||||
if _sudo_available:
|
||||
with open(path, 'w') as f:
|
||||
f.write(str(val))
|
||||
except PermissionError:
|
||||
# fallback for debugfs files
|
||||
os.system(f"sudo su -c 'echo {val} > {path}'")
|
||||
except Exception:
|
||||
_sudo_available = False
|
||||
|
||||
|
||||
def sudo_read(path: str) -> str:
|
||||
global _sudo_available
|
||||
try:
|
||||
return subprocess.check_output(f"sudo cat {path}", shell=True, encoding='utf8').strip()
|
||||
with open(path, encoding='utf8') as f:
|
||||
return f.read().strip()
|
||||
except OSError:
|
||||
pass
|
||||
if _sudo_available is False:
|
||||
return ""
|
||||
try:
|
||||
result = subprocess.run(["sudo", "-n", "cat", path], timeout=2, capture_output=True, text=True)
|
||||
_sudo_available = result.returncode == 0
|
||||
return result.stdout.strip()
|
||||
except Exception:
|
||||
_sudo_available = False
|
||||
return ""
|
||||
|
||||
|
||||
|
||||
+28
-162
@@ -1,18 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import jwt
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||
"""
|
||||
import re
|
||||
import secrets
|
||||
from typing import cast
|
||||
from pathlib import Path
|
||||
|
||||
from datetime import datetime, timedelta, UTC
|
||||
from iqpilot.common.api import api_get, get_key_pair
|
||||
from iqpilot.common.params import Params
|
||||
from iqpilot.common.spinner import Spinner
|
||||
from iqpilot.system.hardware import HARDWARE
|
||||
from iqpilot.system.hardware.hw import Paths
|
||||
from iqpilot.common.swaglog import cloudlog
|
||||
|
||||
@@ -35,6 +29,10 @@ def _read_persist_dongle_id() -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def is_valid_dongle_id(dongle_id: str | None) -> bool:
|
||||
return bool(dongle_id and _DONGLE_ID_RE.fullmatch(dongle_id))
|
||||
|
||||
|
||||
def get_cached_dongle_id(params: Params | None = None, prefer_readonly: bool = True) -> str | None:
|
||||
ro = _read_persist_dongle_id()
|
||||
if is_valid_dongle_id(ro):
|
||||
@@ -46,8 +44,8 @@ def get_cached_dongle_id(params: Params | None = None, prefer_readonly: bool = T
|
||||
if v and v != UNREGISTERED_DONGLE_ID:
|
||||
return v.lower() if is_valid_dongle_id(v) else v
|
||||
return ro or None
|
||||
def is_valid_dongle_id(dongle_id: str | None) -> bool:
|
||||
return bool(dongle_id and _DONGLE_ID_RE.fullmatch(dongle_id))
|
||||
|
||||
|
||||
def get_or_create_dongle_id(params: Params | None = None, prefer_readonly: bool = True) -> str:
|
||||
p = Params() if params is None else params
|
||||
dongle_id = get_cached_dongle_id(p, prefer_readonly=prefer_readonly)
|
||||
@@ -57,164 +55,32 @@ def get_or_create_dongle_id(params: Params | None = None, prefer_readonly: bool
|
||||
p.put("DongleId", dongle_id)
|
||||
cloudlog.warning(f"generated new DongleId={dongle_id} (no readonly dongle_id found)")
|
||||
return dongle_id
|
||||
|
||||
|
||||
_impl = None
|
||||
|
||||
|
||||
def _registration_impl():
|
||||
global _impl
|
||||
if _impl is None:
|
||||
from iqpilot._proprietary_loader import ProprietaryModuleMissing, load_private_module
|
||||
try:
|
||||
_impl = load_private_module(__name__, "iqpilot_private.konn3kt.registration.reg_core")
|
||||
except ProprietaryModuleMissing:
|
||||
from konn3kt_private.registration import reg_core as _impl
|
||||
return _impl
|
||||
|
||||
|
||||
def ensure_dev_pairing_identity(params: Params | None = None, force_reset: bool = False) -> dict[str, str]:
|
||||
p = Params() if params is None else params
|
||||
|
||||
persist_dir = Path(Paths.persist_root()) / "comma"
|
||||
persist_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
dongle_path = persist_dir / "dongle_id"
|
||||
priv_path = persist_dir / "id_rsa"
|
||||
pub_path = persist_dir / "id_rsa.pub"
|
||||
|
||||
if force_reset:
|
||||
for fp in (dongle_path, priv_path, pub_path):
|
||||
try:
|
||||
fp.unlink(missing_ok=True)
|
||||
except Exception:
|
||||
cloudlog.exception(f"failed to remove {fp}")
|
||||
try:
|
||||
(persist_dir / "konn3kt_prime_type").unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
p.remove("PrimeType")
|
||||
except Exception:
|
||||
pass
|
||||
forced_dongle = os.getenv("KONN3KT_DEV_DONGLE_ID")
|
||||
dongle_id = forced_dongle.strip().lower() if forced_dongle else None
|
||||
if dongle_id and not is_valid_dongle_id(dongle_id):
|
||||
cloudlog.error("KONN3KT_DEV_DONGLE_ID must be 16 hex chars")
|
||||
dongle_id = None
|
||||
if dongle_id is None:
|
||||
existing = None
|
||||
try:
|
||||
existing = dongle_path.read_text().strip().lower() if dongle_path.is_file() else None
|
||||
except Exception:
|
||||
cloudlog.exception("failed reading existing dev dongle_id")
|
||||
dongle_id = existing if is_valid_dongle_id(existing) else secrets.token_hex(8)
|
||||
try:
|
||||
dongle_path.write_text(dongle_id)
|
||||
except Exception:
|
||||
cloudlog.exception("failed writing dev dongle_id")
|
||||
p.put("DongleId", dongle_id)
|
||||
p.put("HardwareSerial", p.get("HardwareSerial") or f"DEV-{dongle_id}")
|
||||
if force_reset or (not priv_path.is_file()) or (not pub_path.is_file()):
|
||||
try:
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||
priv_bytes = key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption(),
|
||||
)
|
||||
pub_bytes = key.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
priv_path.write_bytes(priv_bytes)
|
||||
pub_path.write_bytes(pub_bytes)
|
||||
except Exception:
|
||||
cloudlog.exception("failed generating dev RSA keys")
|
||||
raise
|
||||
return {
|
||||
"dongle_id": dongle_id,
|
||||
"serial": p.get("HardwareSerial") or f"DEV-{dongle_id}",
|
||||
"persist_dir": str(persist_dir),
|
||||
}
|
||||
def _normalize_imei(value: str | None) -> str:
|
||||
return value or ""
|
||||
return _registration_impl().ensure_dev_pairing_identity(params, force_reset)
|
||||
|
||||
|
||||
def get_registration_identifiers(wait_timeout: float = IMEI_WAIT_TIMEOUT, show_spinner: bool = False) -> tuple[str, str, str]:
|
||||
serial = HARDWARE.get_serial()
|
||||
spinner = Spinner() if show_spinner else None
|
||||
start_time = time.monotonic()
|
||||
imei1: str | None = None
|
||||
imei2: str | None = None
|
||||
|
||||
while time.monotonic() - start_time < wait_timeout:
|
||||
try:
|
||||
imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
|
||||
if imei1 or imei2:
|
||||
break
|
||||
except RuntimeError as e:
|
||||
if "no modems" in str(e).lower():
|
||||
cloudlog.warning("No cellular modem available, proceeding without IMEI")
|
||||
break
|
||||
cloudlog.exception("Error getting imei, trying again...")
|
||||
except Exception:
|
||||
cloudlog.exception("Error getting imei, trying again...")
|
||||
time.sleep(1)
|
||||
|
||||
imei1 = _normalize_imei(imei1)
|
||||
imei2 = _normalize_imei(imei2)
|
||||
|
||||
if not imei1 and not imei2:
|
||||
cloudlog.warning(f"proceeding with serial-only registration for serial={serial}")
|
||||
if spinner is not None:
|
||||
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1 or None}, {imei2 or None})")
|
||||
spinner.close()
|
||||
|
||||
return serial, imei1, imei2
|
||||
return _registration_impl().get_registration_identifiers(wait_timeout, show_spinner)
|
||||
|
||||
|
||||
def register(show_spinner=False) -> str | None:
|
||||
params = Params()
|
||||
|
||||
dongle_id: str | None = get_cached_dongle_id(params, prefer_readonly=True)
|
||||
if dongle_id in ("", UNREGISTERED_DONGLE_ID):
|
||||
dongle_id = None
|
||||
|
||||
jwt_algo, private_key, public_key = get_key_pair()
|
||||
|
||||
if not public_key:
|
||||
dongle_id = UNREGISTERED_DONGLE_ID
|
||||
cloudlog.warning("missing public key")
|
||||
elif dongle_id is None:
|
||||
if show_spinner:
|
||||
spinner = Spinner()
|
||||
spinner.update("registering device")
|
||||
|
||||
serial, imei1, imei2 = get_registration_identifiers(wait_timeout=IMEI_WAIT_TIMEOUT, show_spinner=False)
|
||||
|
||||
backoff = 0
|
||||
start_time = time.monotonic()
|
||||
while True:
|
||||
try:
|
||||
register_token = jwt.encode({'register': True, 'exp': datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1)},
|
||||
cast(str, private_key), algorithm=jwt_algo)
|
||||
cloudlog.info("getting pilotauth")
|
||||
cloudlog.info("getting pilotauth")
|
||||
resp = api_get("v2/pilotauth/", method='POST', timeout=15,
|
||||
imei=imei1, imei2=imei2, serial=serial, public_key=public_key, register_token=register_token)
|
||||
|
||||
if resp.status_code in (400, 402, 403):
|
||||
cloudlog.info(f"Unable to register device, got {resp.status_code}")
|
||||
dongle_id = UNREGISTERED_DONGLE_ID
|
||||
else:
|
||||
dongleauth = json.loads(resp.text)
|
||||
dongle_id = dongleauth["dongle_id"]
|
||||
break
|
||||
except Exception:
|
||||
cloudlog.exception("failed to authenticate")
|
||||
backoff = min(backoff + 1, 15)
|
||||
time.sleep(backoff)
|
||||
|
||||
if time.monotonic() - start_time > 60 and show_spinner:
|
||||
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1}, {imei2})")
|
||||
return UNREGISTERED_DONGLE_ID
|
||||
|
||||
if show_spinner:
|
||||
spinner.update(f"registering device - serial: {serial}, IMEI: ({imei1 or None}, {imei2 or None})")
|
||||
spinner.close()
|
||||
|
||||
if dongle_id:
|
||||
params.put("DongleId", dongle_id)
|
||||
from iqpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
|
||||
set_offroad_alert("Offroad_UnregisteredHardware", False)
|
||||
return dongle_id
|
||||
return _registration_impl().register(show_spinner)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+80
@@ -3941,6 +3941,86 @@
|
||||
],
|
||||
"req": "All"
|
||||
},
|
||||
"TESLA_MODEL_S_HW1|2014-2016": {
|
||||
"label": "Tesla Model S (with HW1) 2014-16",
|
||||
"id": "TESLA_MODEL_S_HW1",
|
||||
"mk": "Tesla",
|
||||
"grp": "tesla",
|
||||
"mdl": "Model S (with HW1)",
|
||||
"yrs": [
|
||||
"2014",
|
||||
"2015",
|
||||
"2016"
|
||||
],
|
||||
"req": "All"
|
||||
},
|
||||
"TESLA_MODEL_S_HW2|2017-2019": {
|
||||
"label": "Tesla Model S (with HW2) 2017-19",
|
||||
"id": "TESLA_MODEL_S_HW2",
|
||||
"mk": "Tesla",
|
||||
"grp": "tesla",
|
||||
"mdl": "Model S (with HW2)",
|
||||
"yrs": [
|
||||
"2017",
|
||||
"2018",
|
||||
"2019"
|
||||
],
|
||||
"req": "All"
|
||||
},
|
||||
"TESLA_MODEL_S_HW3|2020-2023": {
|
||||
"label": "Tesla Model S (with HW3) 2020-23",
|
||||
"id": "TESLA_MODEL_S_HW3",
|
||||
"mk": "Tesla",
|
||||
"grp": "tesla",
|
||||
"mdl": "Model S (with HW3)",
|
||||
"yrs": [
|
||||
"2020",
|
||||
"2021",
|
||||
"2022",
|
||||
"2023"
|
||||
],
|
||||
"req": "All"
|
||||
},
|
||||
"TESLA_MODEL_X_HW1|2014-2016": {
|
||||
"label": "Tesla Model X (with HW1) 2014-16",
|
||||
"id": "TESLA_MODEL_X_HW1",
|
||||
"mk": "Tesla",
|
||||
"grp": "tesla",
|
||||
"mdl": "Model X (with HW1)",
|
||||
"yrs": [
|
||||
"2014",
|
||||
"2015",
|
||||
"2016"
|
||||
],
|
||||
"req": "All"
|
||||
},
|
||||
"TESLA_MODEL_X_HW2|2016-2019": {
|
||||
"label": "Tesla Model X (with HW2) 2016-19",
|
||||
"id": "TESLA_MODEL_X_HW2",
|
||||
"mk": "Tesla",
|
||||
"grp": "tesla",
|
||||
"mdl": "Model X (with HW2)",
|
||||
"yrs": [
|
||||
"2016",
|
||||
"2017",
|
||||
"2018",
|
||||
"2019"
|
||||
],
|
||||
"req": "All"
|
||||
},
|
||||
"TESLA_MODEL_X_HW3|2021-2023": {
|
||||
"label": "Tesla Model X (with HW3) 2021-23",
|
||||
"id": "TESLA_MODEL_X_HW3",
|
||||
"mk": "Tesla",
|
||||
"grp": "tesla",
|
||||
"mdl": "Model X (with HW3)",
|
||||
"yrs": [
|
||||
"2021",
|
||||
"2022",
|
||||
"2023"
|
||||
],
|
||||
"req": "All"
|
||||
},
|
||||
"TESLA_MODEL_X|2024": {
|
||||
"label": "Tesla Model X (with HW4) 2024",
|
||||
"id": "TESLA_MODEL_X",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user