IQ.Pilot Release Commit @ 27f668a
This commit is contained in:
@@ -201,8 +201,8 @@
|
|||||||
},
|
},
|
||||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
|
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
"sha256": "0b8f118dc45ad0c3809855800960b47c893ee121ea75d94e8569dcce46d58ce5",
|
"sha256": "07b3ace231d9912f1926ffe7cef0f2a5d9649f75b7ebac2b9f296957d98be166",
|
||||||
"size": 7312
|
"size": 7681
|
||||||
},
|
},
|
||||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
|
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
@@ -216,8 +216,8 @@
|
|||||||
},
|
},
|
||||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/messaging.py": {
|
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/messaging.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
"sha256": "17d48331eeeaf746ee6982a9c65f28d740a15a4c34c37617115c104894d49c06",
|
"sha256": "1d345eb35d4eea2a0b8c8e8491b4c1a74e073d8340703cdd334ec78992026f72",
|
||||||
"size": 11920
|
"size": 12033
|
||||||
},
|
},
|
||||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/metadata.py": {
|
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/metadata.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
@@ -536,27 +536,27 @@
|
|||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "f42a1bab373092b3010ca45b02d7ea7b27cd1e1e574c97f701129b2fad01f833",
|
"sha256": "0fec7e56196064f024cf317a6f23d9f2111f90753c48bb7d616e06e27335157d",
|
||||||
"size": 333888
|
"size": 333888
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "306f0362e86558d33072f800b736fa71bfaf4ee5e4383c4e743b72b74724d2e3",
|
"sha256": "4d6e7c4bc97ced0bf2def103d2fc8eb98364747aa33e495bc448c68644739750",
|
||||||
"size": 68064
|
"size": 68064
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "31339f53f093c3877e13eac90a6647cbca2de92f339c47e5006a85bcd2e3656c",
|
"sha256": "a23b14f9f2c5c7a058c20ef0421bb741fc67c2adff13824c1c808e6831430f91",
|
||||||
"size": 267960
|
"size": 267960
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "d5e855d1784a8ba66844639b871d2747ccce264c55359cf0e641ce7e3d66460d",
|
"sha256": "189c7d6d5599f92fc224a086ef55520118130785f482c0667cc808071c84e718",
|
||||||
"size": 203512
|
"size": 203544
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "34d4ef1746143ea714f84f3c325e2ddaecec6a19c544fed68d8fc4080578c73f",
|
"sha256": "2cf0bc9d6362f2d4c9691f4f0ef7989a5651cc3153e99e5c2e15d9da1b93e598",
|
||||||
"size": 399040
|
"size": 399040
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
@@ -568,10 +568,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "7BtEAP11moL2lbQW5dYt1hDqJmDV1EuiEoSfJ0Tfislun2kS+/rwPiy7Ws8kjgrKLv+ZlXlWz6G5IBWk6z+aAw==",
|
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "laKo35uiFJAsuGj7+N5l5IVbXmTQ/jyB01c31OfA7XCT0AG09/cg9ArgDhQv5qr3babesKgUKoPg2vtFnnXyDg==",
|
||||||
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "Sv2riVgvm+4PDVTDLTuy1wpf15JObbP8hSAD4iiwF4RUs49GVGhulXkjzO28mLmF6tEuNDNWHRucxmuZrCKNAg==",
|
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "Sd6VF2hrGHXoXTe3IpCYoTDczM741xcQ1sKiHGcCgNqkeORvLumWxic20ylmWhWOTHziPB9Y723vFOGcKZzDAQ==",
|
||||||
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "d4kaBPAEwe55BrQEVHWmxcVlEoQPsMVLOAYgpZVd6uiBnwWSUxl1X5nmqdKtxWSk8TFqU2xZkhfw4OOgG9JmDw==",
|
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "O9O/B4IDlCutovmEPDnLxdKAFsURjXS+pyaK0OvmoOlOQW1fQS+ZahxaQ0dkwASDzEMWUxUFr0j9PrlihUVpCA==",
|
||||||
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "hvBkDKSMKjpxmRzhhGoRZT5WuOWLY+g3p2NIMlA7b6qYYpVaApoZNuquOerywEF1zJnwPiwpsg5QtQVe/5DfAA==",
|
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "77vKNawQIdv95LPwTFn79N2RXaz6dQUcRtgtnH+R7T2TN2LBSKhKvtzu2sKkKyDG7038qrJfhE9BiJYtho56BA==",
|
||||||
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "d3JAlH7P/X87II6iobeQ51melHy1vxdlXn0Nzo2DG65rcdVLM2B66YETPpwlX6Qv9NxhUlDcl/364CR+V+eyDQ=="
|
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "Fhxo++ZiJmWGBghe/dH/ytHe+T67v/HAA6k4js7Bd+C07dA/zMlgTI45QlBx2fZN2qC602Z4Stijy+/EQziuCg=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
KylnzVsK5XC3Jq1G7bBbKa5lv+oJSlfBpBsNpWMEEUgHDyOEj7+5EeeHDOgy8Lt4LEFgEeIV2WEJW0GJVpxUCQ==
|
nBzDAB5lnwH7Ynehfwf48y7ACh39HIRNiff55kmMtNG466wcxBrkMcnEJz/rDf7ehRcZmyD08CVW5QCfMsHBAA==
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ def egpu_oob_pkl_path(meta: dict) -> str:
|
|||||||
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_policy_oob.pkl")
|
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_policy_oob.pkl")
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
def onnx_cache_path(meta: dict) -> str:
|
def onnx_cache_path(meta: dict) -> str:
|
||||||
from iqpilot.system.hardware.hw import Paths
|
from iqpilot.system.hardware.hw import Paths
|
||||||
return os.path.join(Paths.model_root(), f"{meta['model_name']}_{meta['sha256'][:8]}.onnx")
|
return os.path.join(Paths.model_root(), f"{meta['model_name']}_{meta['sha256'][:8]}.onnx")
|
||||||
@@ -156,13 +161,21 @@ def download_onnx(meta: dict, progress_cb=None) -> str:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def download_precompiled(meta: dict, progress_cb=None, policy: bool = False, oob: bool = False) -> str | None:
|
ARTIFACT_PATHS = {
|
||||||
field = "egpu_oob_artifact" if oob else "egpu_policy_artifact" if policy else "egpu_artifact"
|
"egpu_model_oob_artifact": egpu_model_oob_pkl_path,
|
||||||
|
"egpu_oob_artifact": egpu_oob_pkl_path,
|
||||||
|
"egpu_policy_artifact": egpu_policy_pkl_path,
|
||||||
|
"egpu_artifact": egpu_pkl_path,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def download_precompiled(meta: dict, progress_cb=None, policy: bool = False, oob: bool = False, field: str | None = None) -> str | None:
|
||||||
|
field = field or ("egpu_oob_artifact" if oob else "egpu_policy_artifact" if policy else "egpu_artifact")
|
||||||
art = meta.get(field)
|
art = meta.get(field)
|
||||||
if not art or not (art.get("objects") or art.get("hf_path")):
|
if not art or not (art.get("objects") or art.get("hf_path")):
|
||||||
return None
|
return None
|
||||||
from iqpilot.selfdrive.iqmodeld.model_bundle_downloader import download_hf_file, download_lfs_bundle
|
from iqpilot.selfdrive.iqmodeld.model_bundle_downloader import download_hf_file, download_lfs_bundle
|
||||||
dest = egpu_oob_pkl_path(meta) if oob else egpu_policy_pkl_path(meta) if policy else egpu_pkl_path(meta)
|
dest = ARTIFACT_PATHS[field](meta)
|
||||||
if art.get("hf_path"):
|
if art.get("hf_path"):
|
||||||
try:
|
try:
|
||||||
return download_hf_file(art["hf_path"], dest, art["sha256"], int(art.get("size", 0)), progress_cb=progress_cb)
|
return download_hf_file(art["hf_path"], dest, art["sha256"], int(art.get("size", 0)), progress_cb=progress_cb)
|
||||||
|
|||||||
@@ -248,16 +248,19 @@ def populate_drive_messages(primary_msg: capnp._DynamicStructBuilder, extended_m
|
|||||||
def populate_odometry_message(msg: capnp._DynamicStructBuilder, outputs: dict[str, np.ndarray],
|
def populate_odometry_message(msg: capnp._DynamicStructBuilder, outputs: dict[str, np.ndarray],
|
||||||
vipc_frame_id: int, vipc_dropped_frames: int,
|
vipc_frame_id: int, vipc_dropped_frames: int,
|
||||||
timestamp_eof: int, live_calib_seen: bool) -> None:
|
timestamp_eof: int, live_calib_seen: bool) -> None:
|
||||||
msg.valid = live_calib_seen & (vipc_dropped_frames < 1)
|
pose = outputs["pose"][0, :6]
|
||||||
|
pose_stds = outputs["pose_stds"][0, :6]
|
||||||
|
pose_finite = bool(np.isfinite(pose).all() and np.isfinite(pose_stds).all())
|
||||||
|
msg.valid = live_calib_seen & (vipc_dropped_frames < 1) & pose_finite
|
||||||
odo = msg.cameraOdometry
|
odo = msg.cameraOdometry
|
||||||
odo.frameId = vipc_frame_id
|
odo.frameId = vipc_frame_id
|
||||||
odo.timestampEof = timestamp_eof
|
odo.timestampEof = timestamp_eof
|
||||||
odo.trans = outputs["pose"][0, :3].tolist()
|
odo.trans = pose[:3].tolist()
|
||||||
odo.rot = outputs["pose"][0, 3:].tolist()
|
odo.rot = pose[3:6].tolist()
|
||||||
odo.wideFromDeviceEuler = outputs["wide_from_device_euler"][0, :].tolist()
|
odo.wideFromDeviceEuler = outputs["wide_from_device_euler"][0, :].tolist()
|
||||||
odo.roadTransformTrans = outputs["road_transform"][0, :3].tolist()
|
odo.roadTransformTrans = outputs["road_transform"][0, :3].tolist()
|
||||||
odo.transStd = outputs["pose_stds"][0, :3].tolist()
|
odo.transStd = pose_stds[:3].tolist()
|
||||||
odo.rotStd = outputs["pose_stds"][0, 3:].tolist()
|
odo.rotStd = pose_stds[3:6].tolist()
|
||||||
odo.wideFromDeviceEulerStd = outputs["wide_from_device_euler_stds"][0, :].tolist()
|
odo.wideFromDeviceEulerStd = outputs["wide_from_device_euler_stds"][0, :].tolist()
|
||||||
odo.roadTransformTransStd = outputs["road_transform_stds"][0, :3].tolist()
|
odo.roadTransformTransStd = outputs["road_transform_stds"][0, :3].tolist()
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -176,8 +176,8 @@
|
|||||||
},
|
},
|
||||||
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "aec72c78d58c37afb1b57bea4a29d322032fffdfa59deabdc85b4322b5ca3787",
|
"sha256": "665a6a5163899f37bb7e41fd42fe79ecd77e50496285b60c6555971b8337f463",
|
||||||
"size": 67616
|
"size": 67568
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/__init__.py": {
|
"python/iqpilot_private/models/__init__.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
@@ -186,38 +186,38 @@
|
|||||||
},
|
},
|
||||||
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "30a25ebd3b37e2307c3a80ae0fea70b314ab71427d313ba5e2d540c4ba7658ed",
|
"sha256": "8a63da11107132869d7ea4e391197ca8bde2d13d6d92b09a83484503910a75d5",
|
||||||
"size": 133840
|
"size": 133856
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "e8cb29388e31f636d19f916d5a1d32f38c38113f5fa882344d4effceb6e37d85",
|
"sha256": "7090be17ce2063905fc364ee925384708895150f4d6eef642a94b941d3c6c88d",
|
||||||
"size": 68112
|
"size": 68096
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "b7938d9521352d5c1fa6b0d7ff8f3f11062e33bcc0d9a72ffb0edfb525423b6e",
|
"sha256": "c2691e7a3e77e78d20d21cbf0d0bc4d59fd294935c7ebd0cc7225b0d77619914",
|
||||||
"size": 68264
|
"size": 134032
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "dad0c3c3a73338647da25dead17da0db6cebb24527bc9db381cd16753741f676",
|
"sha256": "d085741bb6a7d2a94d616d53e78e0ae76d381ca926017646ec33931375fb5963",
|
||||||
"size": 136200
|
"size": 136200
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "ce815034dd8bdadb79131e0b51013a9f9772c4421dfb96b62431d84125089ad2",
|
"sha256": "951ecc3665d6a2232f50ca3002f7f84753789ecd1adfc06cc85884b42c47e05c",
|
||||||
"size": 68056
|
"size": 68032
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "798843f52120210c19da5b6c98723ca15d782ea9cd445cc8f96009471990d618",
|
"sha256": "8670d22574d59c9b2f43c0fa047967a3236fc4eb0ca315a61fe0c9f2636d886a",
|
||||||
"size": 137944
|
"size": 203512
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "96be19ef4f59b87e44092c61f70676468617365923c4c8b91cf7e0b88cc62c05",
|
"sha256": "c0fcfd5193cd11e52f22d8c804ace3b436f49aaa6655689c2f1903931544a977",
|
||||||
"size": 340000
|
"size": 339976
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"entries": {
|
"entries": {
|
||||||
@@ -228,13 +228,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": "CHPHmtjVAGTdtbWnwE5awrEKFJHs06YK4Gao9IKVaHZYXyj5WAc/d4t7hOKM3lm2/UfBaraTdVTdeSOij3dpCQ==",
|
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": "BaGn+mLXPAGLqnVJhDUt8C2ZEm1E9+MUCPoOnJhQGQybFOr36MuoubfpEgPoy1993Tm4IGD3pZASyFefJNXtBQ==",
|
||||||
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": "Ekiv6tp3mDx+7Z4lNI+byVomzjShe93UkYQEOhYwl6kHlpxN06A3IqV8y5pwlJII4ZpE5yYEX9qp+VMXsdm9Aw==",
|
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": "DfeUTDggp3zRuVHoECcYdCliGhB1A/BdQoicWHddyTS4YptXxaow5Rads9qnmRIiBpSgR8d0vH9fwNxpQl0aBw==",
|
||||||
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": "njYujmFBsfewILpjLI0nUN34rqzMNITattpewwxE4LuTcdkAgihQ3w/S/aYetSJzUtV/SH9mk26DhMciwn8KAw==",
|
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": "9sIFDj6iIVEUE+lxa3Ryk/djxgiPMu78MM/WQdjYfpVQNv2OQ7IkBu121iNa5XIv/CTFMl1G6KGTWO5/Qm9iBw==",
|
||||||
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": "zE2s6oQ3YqanaFG4RMNPydekiblRQVBk7QEJ5G30W2thGqzWt+1BW+75XdwaCigw//rINxKHbt3Neu5rub7PAw==",
|
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": "CjvP1I4QBJu6zRW75XHEUWX6F6Wks9xtnuu1hdjv/V2Vw11Vf+Wa8zCztpKBgMx3dzbQ/p++xpTf5p7TCoMyAA==",
|
||||||
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": "hBKd/smbmwEszsGjU7FP2RNVmrINBpU4NPZMGDjEHAF7Rx1WBF49+pH1s0k571RBqdtqN7nya2HCnKKO0tHUBw==",
|
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": "WNq4sNwhPeu7NuW+hj1UOv1lAiQN2k8lPwImfeS7vFLY0G+pUBpnCVhD49SmjMmPyfCoxJkxD+wdhogPT2u6DQ==",
|
||||||
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": "eKa9qfsfNNaSQpqggWSGw5YV/6cJk5iJcmhY+N/v3xdFvEMfCsIf+N3BhP0S3ZDO5T2CeE/I/Xc+1QbZh7oNDQ==",
|
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": "FJ7yptMwvQxz5kB6XLjNJKBDy3+jrwWsPe/KBa8ki/e6uNavH63Umu18z5e6aY4AYp9D8z0pBOt5lk1X+bFvBg==",
|
||||||
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": "k4BcKQAua+1YSC6P8jVEu2wBrI+WL8Bb5kKQbOnBYn/lupnrA96vMRYu4XVVDLGBoIqK8L7K/PYCYxu9Cx4RDA==",
|
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": "9sX2Peumx+BT3G+LRIbPtcs42mULnRiXJMbQq3esj7yfaGsbf3l5XQ/9BDr8U/A6aFWfatOYSwbFD37ZGdLxCg==",
|
||||||
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": "+53NsV73jkF/1x9Bzsw99oZRhVBtqlQTeXaPfHydTAssd2V3UMO+BWqagtGbVG5qIOcgjaNweNExGu+Abo7iDA=="
|
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": "uyUgRSqgfhMcboJeDLTF1vAuw6thTLubkKp4wsn3i3VFAGkLzE0fEkhzoRo1TSn8hkb787kNQs+Z/woCPxi5AQ=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
8t0c8OQ6AY5JlVXqGQkiF2rlj3kN3MIwGHVz4TOgaudkW7XegnRhUqngcpNo5C74wFDKNGWsvXOJ4WtIiIrfCg==
|
aZ2PpZxlTDJ1Nffu+hFtfKK0qyUkkllomgYgTX69J4YU4/aODiwBvJDwHZ6+1nlvv4fDA10s7SPNxwwP9j0OBA==
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -186,8 +186,8 @@
|
|||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "6e6de2f3270ad097c9b847fa10e833bac6b8eaab8ddad955603d163474a3c846",
|
"sha256": "10896807faa5044f6d42807fb7e5efefc6ef64243edea9da5639332964e1cb19",
|
||||||
"size": 67616
|
"size": 67568
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/__init__.py": {
|
"python/iqpilot_private/navd/__init__.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
@@ -196,123 +196,128 @@
|
|||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "516370d0603f5134955826a3d8e9fea8ec0a9d27c116ad05af149a5c7ac15bcc",
|
"sha256": "5faf834f012075671a390de2fe4ebb09f9c7d4c05406fa89407aa9e011e9d38c",
|
||||||
"size": 269936
|
"size": 269896
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "dc8f27970c61a3b60c383ea69804896ee356d76c8bf7af5532a9338525fbc956",
|
"sha256": "a8361fe53878b61d30d430d0b0488eaabe1641e11b7e584e1ad3566370e55085",
|
||||||
"size": 202040
|
"size": 202088
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "96eb02a86cab08d557f8df0adebd2cc72232b9348e9896acd9b71784a86b42d5",
|
"sha256": "47c673a898e6ceb321f01eff980250bbc85ddd058a8161098e8e91cb02f8f4a6",
|
||||||
"size": 136960
|
"size": 136928
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "8fb8cc47f47971c530a68ccd81b9b5d9fc8ce82cdd1270967fa845399009e519",
|
"sha256": "55a8b9eb2e17f9baa1a851919d3ce908b69004bfbd09591985195af2da4cc40c",
|
||||||
"size": 135960
|
"size": 135928
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "b49f7ce7e9a33777ffa896a95fc4a5580988b4966d5a9330efd8c79a5116be91",
|
"sha256": "115b57594d411fde87f66d0d030cd5de043bf8d986274dfb8fa7007b8f752c87",
|
||||||
"size": 135584
|
"size": 135496
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "747ae769bb35650dcd16b94a0a6260213373e352ede88bc1e57f114f27761d5a",
|
"sha256": "90c8b38b5c0a328c9ccce27292f8399546a237cc72caf837ffa4c1a55a529c47",
|
||||||
"size": 135632
|
"size": 135600
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "eae425cd9bf60286fff4b980c6057351cb940f28981556269f05022258d80f0c",
|
"sha256": "6d80f03b5a4da8083d4b7f2d4b165748556230859bcdb96c2ff5164aa5500b6c",
|
||||||
"size": 69752
|
"size": 135256
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "0fc4356fe57a11538e4a248dc803ceee61781d7c700640f78241a2d57cbb4574",
|
"sha256": "a6809b5c118c603706e6b709597a29daa2345fd53d736526f6a055a009ad70ac",
|
||||||
"size": 68144
|
"size": 68128
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "92521da14a5bf334324f32315b13773d37d5f7d209d4bad6d62afc12ab5d1705",
|
"sha256": "05f7b0433be88afdae23a68f7acd92ff0c8ce6b04d83fc46ad2233c3df51dea0",
|
||||||
"size": 203056
|
"size": 203024
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "67454e461abb76c7a3053a7f728baa0beb30fba235639a0a2a927a9c80443a4a",
|
"sha256": "96414985cacbd85d02f69473b84ff8215fbf959996ca1f1f0dfdf7c77503c9cc",
|
||||||
"size": 135560
|
"size": 135536
|
||||||
|
},
|
||||||
|
"python/iqpilot_private/navd/navbridge_source.cpython-312-aarch64-linux-gnu.so": {
|
||||||
|
"mode": 493,
|
||||||
|
"sha256": "8bd53fba3f00e3f8320f873e72b3665f22b6065ee10425d270167b21d2348ad9",
|
||||||
|
"size": 136576
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "9e0d5e929bfcd960fdba11804d1f70848ba40a68a65093c025a6d48171d0b2cc",
|
"sha256": "d395b00b81da1acdab40d6033b5ccf084effcc6531978af63d568d4ba17ee894",
|
||||||
"size": 797392
|
"size": 862920
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "33d40decd32e8cfe3f9a267ebfccf48e8bfc67bf9609206ef230a9566bf5581d",
|
"sha256": "43bdd7f93c317f26bfa9203953b3a35bf91bd0475dc3777aa5fc3e991216c3f4",
|
||||||
"size": 135344
|
"size": 135464
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "edf0d16511686ed3d4d7d6bf2b6b44fd8d484af348dab0eb415999656eb1d0af",
|
"sha256": "4678f2e256e0fd2b01d4441af848056b173d7d2d6c8ba590ad32b8e23ee8591e",
|
||||||
"size": 135584
|
"size": 135552
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "b11bec6f1b49f169f218a87467781b6d62d64c367e0c3df5356341adce8c3f6b",
|
"sha256": "52e4d03639c9d9be88209ab2e4a1f0f23c0adf81631983248f7d069d46cf61a5",
|
||||||
"size": 202384
|
"size": 267936
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "d38218a6787234730ed39bdb04c00ab0778b1b56eefcedcb9ecfb31346d36378",
|
"sha256": "cb066c1e5637b8a6b60b44753b6a0855a04ce7f5771464d71fc9da245de4a56e",
|
||||||
"size": 136216
|
"size": 136224
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "f89f69e128b02eebc31b4c3702d62e12747f837863ad390026ea5004e506ea7c",
|
"sha256": "2e226e29757151bc3cf350a933f6e8f402d7149b0b9e66b82ac9cb721cadef8e",
|
||||||
"size": 203136
|
"size": 203096
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "311926ffa2b4474320411240e6656ee676ce8b61d1cea444c34cd9520113a6a6",
|
"sha256": "2b12153db93198ec46c268384cfbd65a9ae526faa584274987ed8a289531547c",
|
||||||
"size": 943640
|
"size": 1009128
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "dc08b2e6bf7be3fe0b4a7ccdfceab6c434317e7626efd9b40ea5bf15739647ba",
|
"sha256": "6ba9e9ae810a4e283a8ad544e1f42e899e2e98939754f9b88ffc21f320a8514f",
|
||||||
"size": 135760
|
"size": 135752
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "f47146eddd7c8b499359d411150533c0e28e80851096d076ee8552a4d45175d9",
|
"sha256": "0e233af11a3c07d6469e221b5f02cebee4addfe1706d635f8ed7624eb16ff908",
|
||||||
"size": 69720
|
"size": 69688
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "98845f8be1fd44009acf88fe685f6985ffb996d5d508858bdbb3edd2984a5146",
|
"sha256": "8485347d40907cd0e654b93e1e872dffd156278c3868c640f7d2e51884d52a00",
|
||||||
"size": 135968
|
"size": 135944
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/traffic_writer.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/traffic_writer.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "15aba494968b9673987d2d0168d797bfbd307046555cb89efa2378acc8bb042a",
|
"sha256": "c8b907c51c69c7950bc99ff577ddf4473dbb42f90e53252e3dd325df2a4f9ed3",
|
||||||
"size": 136936
|
"size": 136944
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "715f94a985599a983750affa5bf36e1fc2e6502871f1af7de553f9aaf29028cb",
|
"sha256": "c13b5fdec4a8d4a33dbd7c25d9ffba30f6faffa1d286438d80c4d9a615b31145",
|
||||||
"size": 69768
|
"size": 135280
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/valhalla_process.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/valhalla_process.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "2658d1a9f08f49e1294a3c26be733170ac7943970a79ee2969d9404cd4b55758",
|
"sha256": "695322ac198a75121f05989387bd32a733f18921f01f373d45e22f7ec839e8a5",
|
||||||
"size": 204072
|
"size": 204064
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "d03bf4d8d7d1547c5fcd085dea2ff78f8071e04e51e7f4d002f7ba23b431038e",
|
"sha256": "24d967c264e1b879b0517da9cd6fddefdc4787efe6a52adcd2429efaac2db3db",
|
||||||
"size": 201400
|
"size": 201368
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"entries": {
|
"entries": {
|
||||||
@@ -351,30 +356,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": "9i3/SfFfCLGyTzyQQYpsSxbA3ASc1OSK7fCYXpIiIMnX+jKB288Qy7TzJnr3uINt+LhRW1wwnMI6hWBXjgv5AA==",
|
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": "3zeWvLW0gfxiEeHYfOO7atzH0V9/YxbbPpLV6cdokoSP+UlxRJUrMTkJiNCiLBzXuhnNbEhj0Y6q9dLK8QNDDw==",
|
||||||
"python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": "5K4w4dDRtts8QvxBAXL0qrYofb/bg9PV/rKn/GrVmtAVDMd0DhAiKAugoxkhl8n4JviFY8ZmsHwWBrQPIMdkAA==",
|
"python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": "1vJWjDPECvOrFe5UfWFN3rFeZSfO3Te9o3Xj0ue1Q+2Zt+gMkvaXgOZG3DNfbs7yaZ2yheabOhKRdwvwHGumCA==",
|
||||||
"python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": "X+hzm32FyMwE9fSIaGZH/GQqHn78izqrX8o1FdATb0Fg4+62tcgI9LHe9PIM8VY6+IaryaWyKqFtATaq6BZrDw==",
|
"python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": "6IWRbqN7iDaCKDPQ3Ho6cEhWrP0Tp6f5nKoo5sXEEAtYfUJVZADBHXY7Qk5MszNoa13Sn+8H2eE0imUwjGUtDA==",
|
||||||
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": "dQnA8CCeRMXqiARr/Kzf2x736DuaT1/ALC8ZLDZep2/nY2IJ7StZeZ8DwOBuRPRK8KJ5HNU424QMZkVzQcDnBg==",
|
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": "gakLE/auDWpkOm9vOzHryjZm5dAIln/GbASMoFl4iMiMRVmFmBjdV0uyY6bkVDKPVHX6FRr65f1yuJbF1d09DA==",
|
||||||
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": "tdLykhS2t9M7R115Rse80M2Urg/8sNQTLaOz3YFyUTTkEzpaBlk3JnbgOQVpzDjrPB7y22y6mNORCkYI3cKfCw==",
|
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": "yOqbMSdUVZSlHAiw08Yb+vSyEr2YoHBsSIWtFQa1RqoSmLbeLUeFpTiuczVaW/HtCNeZZU8mJYXWyTUM1N0xCQ==",
|
||||||
"python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": "MJ/i1ZvGb3bYBVxKvT/zBk43CM/XT37GZa6la52Tfmqp9mrx6oz98PpvLZMgJ+18J4lSdMf2uGuJ2IHx5wWTAQ==",
|
"python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": "8uEYXTWYXVzhDctzoEBbSc5wJ0mqX8qAe9DuXeKgfmpPsM6rR8jHpjHiyjkLAAPorWDRVN3a5W3iyH2u2tD6Cw==",
|
||||||
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "+ULQz9iDSDBtYRYSVVWz63TImB3DizGV2oFER6pmNttfJ6vA4SBVToxKV1YjHAebdMiSrTBrEkgPd5K4unE0Bg==",
|
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "+kTCQnh0TKGtTVl/XqtKJfrFDVJloVVhsUmt793WbvaULUK9aAJmtaEJRvTNV/DiOjYGdgL1texfnBw3x3CkDw==",
|
||||||
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": "PB+q882Oo/cOi+fN0IahlaBP7xEQrjF7BykAnCsxrnK8zJGkFZdrVUcLCS4T5DYjXhwlXbdveb3ZHdG/LoMcBQ==",
|
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": "oFJS3s2xxAmMxBpLAiaFklhstR8719Ah/GtUy+YMPdaDZBP6TYLIe89aqmPjW7LUHagkmxsJogppJXl3YtFUAg==",
|
||||||
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": "vqQRU0t5AS/oUs1JWxZlwHC5mrpaaHD10jgQCLzDGUL4Yv1aFHWAoM6KfFdUldmdSG1d4e6VGu+Y2bMdBIwAAw==",
|
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": "rsDD81jJXWuvS+IYSDwKVZy/C2lvnmmEh+wYucqLuUg64BIW5Q8YpdtRM9Z++fYpSut90FeYHtrIJsAavTmOAA==",
|
||||||
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": "OfuUD6/swlx5ggtvLfUVjGqpJbZWf+Ezwia5fE81FXA6RcuR4SMx1t/D38+TaMKDGvaBV31s08TJgh+/hXiiDA==",
|
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": "5CiNWqZqmmiNfnj1plhm67WEF5PXkPGr6dMsVui4VplsrVwaWCSQYJ4ywPHlsoUeX+c4FoCX7HJSNqwxjn5IBg==",
|
||||||
"python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": "WQOzntcyeraddagnvnxAwH/ciLFMztjfEhg07HFHy+++XJPCSGctdaxlgPWX6xq9kiE7aEkcTsW2CwOwYo3tDw==",
|
"python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": "dkNKNQEHnq0Wzw/Usmwf/qNxN72cpBZBnPHRKN1ZEq5nExMqrrraTe6bn+EWM2yo1hMsXsep5JPpNGxpqqusBQ==",
|
||||||
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": "5c68ZgFSkAXf9m32zKbBDSNHF0rVTwZPVu0puTiIcM7laHyHVtkQJduq6g4SUm+wz634nLK2u5pggaNf+mkhAw==",
|
"python/iqpilot_private/navd/navbridge_source.cpython-312-aarch64-linux-gnu.so": "0PDFbzaD2KoWZRAP6I38SKdUG0T11j86Ue27RgjziWLAPuxDSd6ogJPbTuPxKLV6pBIASN6y1KU3Cf4BLOkKBg==",
|
||||||
"python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": "LBV8K1SPj+bN8idsPR/3B24grXQYRNarYsCUbVaRdkfORuib70gRlGz4KtyOyYT/vpCU4iuDhnSRsd4ltH4RAg==",
|
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": "WikwGS5js59suyyzVj90X0dYz6I+wBb7N/KHRGkD4kYEX59jP8hAK+MlLsUAr9Vbmss131S8z5inlifsCjN4BA==",
|
||||||
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": "ZT4bVD2W2w9w2AdbKkZ7AgfQv+zqTmJH5126ngiDDRFFuW2Bnp+E/4M+6U912uGZsV5xayCw8QmJbM9EeSqgDQ==",
|
"python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": "LOnh9gEJeT8leh4xo0gabomCwTOo1Xhp2oALjrsL0Ij1MCur9bLl5QE33iFVcomQr8jGH0iw7Vq1vXDMlIRuBg==",
|
||||||
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": "jjmBJtLWRVkPsO4n7/N/pDVD9jrC4SCMm0lIT/P8HCzsMhIIAkXhRAMGKdl1kYDxK8VMGRMMF7eoiCKEFPq2AA==",
|
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": "KthwWou6yvP+17m4g/3PGY7aPTKKjUqLT8KTtsiTr8/y3Le6YTntbn680C1hJU/xKm1j+IcnAomDw7wdma1PDA==",
|
||||||
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": "A0FeGUh384rTKZJLOL6wNdX3bLTTsmUX7kF3ysax7WjnyUSlctbms15E+3Ww87qmXwjMGbyPdlTdsnhvpcstAA==",
|
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": "cdd78rET10BlVNmGXsNJgYpbS9VCSNEB5HJQ9krzeMu05ikqVnI7vHA/zlxmuHHFiHnZyAN00c16mEmgf62dDw==",
|
||||||
"python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": "J/I+XcODFE7LKvtyImt/Dw8fh2uvQeEhZ1IWx2IHZPH8sXhuVGVQIECVFGQJGfsckjzoUpgZq8AYNReRF62sBw==",
|
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": "QyrJUPOGnI39p3n24ZCKdHG+MI/bqIvBwUdFyGO527eqHVVWJyinfneK+X7vARV+HLwxqO5bBcgwqHLdi9enAQ==",
|
||||||
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": "n/RFD32mlinaYGrWvKWGv8ziTTSFP8+qCS/I1ibFXRYU6gtwx/+03eUR+p2jh5hzPteo5RxLUfHDwxFvJMJzDA==",
|
"python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": "YkM9q3yJaDmYhExd3Te/7tpFQXXrt+/82M0QRZrVxjKRKOxrR7xu3jdjXQIT67I6WxpHzsNIFhnzX2Bl2/9eBQ==",
|
||||||
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": "/5mYKn9um2aBLipkFsEptfM3sOId3SKkV8AHXGslWdH9Evp2ll8XENvirAX5tV1X35WslGYIybK3Xv3cZ4o8DQ==",
|
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": "+Uu9KthRwOh5+YJaj1/SD/QuSOfEuT0d4CwO//wB7sCueUKsVx7+CcyAC5T2YB8bRN+42NJDn8pQeA9JcrPoDg==",
|
||||||
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": "kfl1Mm7G3CguQGtUvDutvbd4StDnKr0pW7ojLn6gydVTyxEiD/VAEFfTKBK/CAyUcw3eeX5+U8Qq2+6o0zEUBA==",
|
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": "BoQYpXsWgO+RdUhI5HL4WSk8La2HJ8V8DSFWvAylCaDNWJX3yaClso603Nf0CcKd1MDycb8J33hiHvJSd7R7AQ==",
|
||||||
"python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": "SzicVJ2/F8aMjcOHVCmEeJ952a+JPL8NLI6wsVkA2f5xiOSKMdynvcf7EgvrvBA4bgTRUGOmtpoqzMiCcNh2AA==",
|
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": "5+ip8rEk+Upsvyp2tlfdVhmIdQnlamloY1SCuwFzkSM39y+W9tvo4r7JTeb5pcMiUijmwK54m4Rvx0XWvxz5AA==",
|
||||||
"python/iqpilot_private/navd/traffic_writer.cpython-312-aarch64-linux-gnu.so": "tMmnyV9knGA0eoRq1+bYLaQRAmLLrpSC9KraH9iNWMgleg+xdY3dIpx+uyP77de/oOQ2Bovsp3Vq2MobcDACCQ==",
|
"python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": "WtPKZHGDG8iQj7q1UsXw6jTXxJsWLfNaw9kfJ3huWYjxWcHz0K7ibJ5JzEGSc5l5u3LdRkt6/aZHlBMrDPBaBA==",
|
||||||
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": "kFb5U48pTMYRWee+JbH6CbOpE2je60Xk5ntJZIUCw66KWLTOUGFtQqMcUUcQ1b2/3xnCz7sb3Yt0GdbyyspMDg==",
|
"python/iqpilot_private/navd/traffic_writer.cpython-312-aarch64-linux-gnu.so": "VVFiWnkiwn7nwhq+6q3aYsnxMW1GLdfaGB/a/h8JV46OoSn0NOTVA72jPYlzs0d4bacEN+IgMAelCjnMlqiBBA==",
|
||||||
"python/iqpilot_private/navd/valhalla_process.cpython-312-aarch64-linux-gnu.so": "u6o5lRGFT3PA7XPM0IXyr7iEsngyLKqh+kJhHNW7U7+iTI3hH25ag73YFQjnCfIuzPk2NtB4v2JKqcqBaNaQAQ==",
|
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": "upuJWa/0B6du9cu3JwdzK1cSqSAORKfvd6ir7sh/oWSRPEhkxyjPmo9/uoNw4Qeq2zoh3iKkV2udK5gue/PzCQ==",
|
||||||
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": "4AB7oakyIWLG7OFuGpO+SidZnJzq3rC7/uWemSbhFVn9oOZBMQZ5IWN5Y3WfImyqoZXjZVGqxE9Yf0d8gmWdDA=="
|
"python/iqpilot_private/navd/valhalla_process.cpython-312-aarch64-linux-gnu.so": "DXSI5ljQTXPHV+QvQCQu1+oOfLKcPXMkx7qx6id0NTFPHI/M5KIooBYKgJjbN4abl+mzUXKrtOmGF/y3f0lwDw==",
|
||||||
|
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": "h4bQodknw1y0emCI3aJYviy3kzaQH8hElJMFmpS/sMvvC+sCHfOGUo+8BK+T7JGZ9/VzNXBzP8RKkHG5cAf0Cg=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
NH/aexXGu1jWh6ks/x7lbVEUX9VZPhETrwkIozXSjmPR2isUyVlaZppveq23NB7BeIQTJuS/+KAuZ/pbL/kaDQ==
|
kmjHw5sbr1uhbckWG6FoCjTR0idPL+RGlQ19Wri9phayqsni9P3V2rqLDUPeZ0ds+CRqMb378Jgghj6eJ6gxCQ==
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -238,6 +238,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"IQEmacEnabled", {PERSISTENT, BOOL, "0"}},
|
{"IQEmacEnabled", {PERSISTENT, BOOL, "0"}},
|
||||||
{"IQEmacHost", {PERSISTENT, STRING}},
|
{"IQEmacHost", {PERSISTENT, STRING}},
|
||||||
{"IQEmacModel", {PERSISTENT, STRING}},
|
{"IQEmacModel", {PERSISTENT, STRING}},
|
||||||
|
{"IQEmacSmallModel", {PERSISTENT, BOOL, "0"}},
|
||||||
{"IQEmacCatalogCache", {PERSISTENT, STRING}},
|
{"IQEmacCatalogCache", {PERSISTENT, STRING}},
|
||||||
{"MacModelDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "1.0"}},
|
{"MacModelDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "1.0"}},
|
||||||
{"MacModelStatus", {CLEAR_ON_MANAGER_START, STRING}},
|
{"MacModelStatus", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
@@ -315,6 +316,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"IQEmacEnabled", {PERSISTENT, BOOL, "0"}},
|
{"IQEmacEnabled", {PERSISTENT, BOOL, "0"}},
|
||||||
{"IQEmacHost", {PERSISTENT, STRING}},
|
{"IQEmacHost", {PERSISTENT, STRING}},
|
||||||
{"IQEmacModel", {PERSISTENT, STRING}},
|
{"IQEmacModel", {PERSISTENT, STRING}},
|
||||||
|
{"IQEmacSmallModel", {PERSISTENT, BOOL, "0"}},
|
||||||
{"IQEmacCatalogCache", {PERSISTENT, JSON}},
|
{"IQEmacCatalogCache", {PERSISTENT, JSON}},
|
||||||
{"IQEgpuDisabled", {PERSISTENT, BOOL, "0"}},
|
{"IQEgpuDisabled", {PERSISTENT, BOOL, "0"}},
|
||||||
{"IQEgpuEnabled", {PERSISTENT, BOOL, "0"}},
|
{"IQEgpuEnabled", {PERSISTENT, BOOL, "0"}},
|
||||||
@@ -359,6 +361,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
|
|
||||||
// iqpilot model params
|
// iqpilot model params
|
||||||
{"CameraOffset", {PERSISTENT, FLOAT, "0.0"}},
|
{"CameraOffset", {PERSISTENT, FLOAT, "0.0"}},
|
||||||
|
{"IQAndroidNav", {PERSISTENT, BOOL, "0"}},
|
||||||
|
{"IQAndroidNavStatus", {PERSISTENT, STRING}},
|
||||||
|
// "waze" or "maps": exactly one runs, the other is disabled outright.
|
||||||
|
{"IQAndroidNavApp", {PERSISTENT, STRING, "waze"}},
|
||||||
|
// Consumed by androidd to sign the container in once, then cleared: the credential is
|
||||||
|
// never meant to survive a manager restart or reach a log.
|
||||||
|
{"IQAndroidNavEmail", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
|
||||||
|
{"IQAndroidNavPassword", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
|
||||||
{"IQLiveSteerDelay", {PERSISTENT, BOOL, "1"}},
|
{"IQLiveSteerDelay", {PERSISTENT, BOOL, "1"}},
|
||||||
{"IQLateralAccelSlew", {PERSISTENT, BOOL, "0"}},
|
{"IQLateralAccelSlew", {PERSISTENT, BOOL, "0"}},
|
||||||
{"IQLateralCurvatureLookahead", {PERSISTENT, BOOL, "0"}},
|
{"IQLateralCurvatureLookahead", {PERSISTENT, BOOL, "0"}},
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ class Controls(IQControlsLayer):
|
|||||||
|
|
||||||
actuators = CC.actuators
|
actuators = CC.actuators
|
||||||
actuators.longControlState = self.LoC.long_control_state
|
actuators.longControlState = self.LoC.long_control_state
|
||||||
|
actuators.speed = float(max(long_plan.speeds, default=0.0))
|
||||||
|
|
||||||
if not CC.latActive:
|
if not CC.latActive:
|
||||||
self.LaC.reset()
|
self.LaC.reset()
|
||||||
|
|||||||
@@ -63,6 +63,11 @@ def resolve_model_name(params, keys) -> str:
|
|||||||
from iqpilot.selfdrive.iqmodeld.egpu_model import DEFAULT_EGPU_MODEL, resolve_egpu_model
|
from iqpilot.selfdrive.iqmodeld.egpu_model import DEFAULT_EGPU_MODEL, resolve_egpu_model
|
||||||
resolved = resolve_egpu_model(params, allow_refresh=False)
|
resolved = resolve_egpu_model(params, allow_refresh=False)
|
||||||
return resolved["key"] if resolved else DEFAULT_EGPU_MODEL
|
return resolved["key"] if resolved else DEFAULT_EGPU_MODEL
|
||||||
|
if params.get_bool("IQEmacSmallModel"):
|
||||||
|
from iqpilot.selfdrive.iqmodeld.models.helpers import get_active_bundle
|
||||||
|
bundle = get_active_bundle(params)
|
||||||
|
if bundle is not None and (bundle.internalName or bundle.displayName):
|
||||||
|
return bundle.internalName or bundle.displayName
|
||||||
name = params.get("IQEmacModel") or b"lebrowski"
|
name = params.get("IQEmacModel") or b"lebrowski"
|
||||||
return name.decode() if isinstance(name, bytes) else name
|
return name.decode() if isinstance(name, bytes) else name
|
||||||
|
|
||||||
@@ -168,7 +173,7 @@ def _patch_and_send(pm: PubMaster, payload: dict, frame_drop_perc: float, select
|
|||||||
if mismatch is None:
|
if mismatch is None:
|
||||||
mismatch = source_lag > 0
|
mismatch = source_lag > 0
|
||||||
|
|
||||||
big = payload.get("source") in BIG_SOURCES
|
big = bool(payload.get("big", payload.get("source") in BIG_SOURCES))
|
||||||
model_msg = log_from_bytes(msgs["modelV2"]).as_builder()
|
model_msg = log_from_bytes(msgs["modelV2"]).as_builder()
|
||||||
if mismatch:
|
if mismatch:
|
||||||
model_msg.modelV2.frameId = target
|
model_msg.modelV2.frameId = target
|
||||||
|
|||||||
@@ -272,6 +272,23 @@ class TestChannelContract:
|
|||||||
assert sent["modelV2"].modelV2.frameDropPerc == 0.0
|
assert sent["modelV2"].modelV2.frameDropPerc == 0.0
|
||||||
assert sent["cameraOdometry"].valid
|
assert sent["cameraOdometry"].valid
|
||||||
|
|
||||||
|
def test_selector_big_flag_follows_payload_then_source(self):
|
||||||
|
from iqpilot.selfdrive.iqmodeld.modeld_selector import _patch_and_send
|
||||||
|
|
||||||
|
sent = {}
|
||||||
|
|
||||||
|
class PM:
|
||||||
|
def send(self, service, msg):
|
||||||
|
sent[service] = msg
|
||||||
|
|
||||||
|
payload = make_big_channel_payload(42, True, 0.03, 25.0, self._real_msgs())
|
||||||
|
_patch_and_send(PM(), payload, frame_drop_perc=0.0, selector_dropped=0, target=42, source_lag=0)
|
||||||
|
assert sent["modelV2"].modelV2.big and sent["drivingModelData"].drivingModelData.big
|
||||||
|
|
||||||
|
small_on_mac = {**make_big_channel_payload(43, True, 0.03, 25.0, self._real_msgs()), "source": "mac_big", "big": False}
|
||||||
|
_patch_and_send(PM(), small_on_mac, frame_drop_perc=0.0, selector_dropped=0, target=43, source_lag=0)
|
||||||
|
assert not sent["modelV2"].modelV2.big and not sent["drivingModelData"].drivingModelData.big
|
||||||
|
|
||||||
def test_selector_lag_patches_frame_id(self):
|
def test_selector_lag_patches_frame_id(self):
|
||||||
from iqpilot.selfdrive.iqmodeld.modeld_selector import _patch_and_send
|
from iqpilot.selfdrive.iqmodeld.modeld_selector import _patch_and_send
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ LONGITUDINAL_MODE_STOCK = 0
|
|||||||
LONGITUDINAL_MODE_CHILL = 1
|
LONGITUDINAL_MODE_CHILL = 1
|
||||||
LONGITUDINAL_MODE_DYNAMIC = 2
|
LONGITUDINAL_MODE_DYNAMIC = 2
|
||||||
LONGITUDINAL_MODE_PILOT = 3
|
LONGITUDINAL_MODE_PILOT = 3
|
||||||
|
IQ_LONGITUDINAL_MODES = (LONGITUDINAL_MODE_CHILL, LONGITUDINAL_MODE_DYNAMIC, LONGITUDINAL_MODE_PILOT)
|
||||||
|
|
||||||
PERSONALITY_AGGRESSIVE = log.LongitudinalPersonality.schema.enumerants["aggressive"]
|
PERSONALITY_AGGRESSIVE = log.LongitudinalPersonality.schema.enumerants["aggressive"]
|
||||||
PERSONALITY_STANDARD = log.LongitudinalPersonality.schema.enumerants["standard"]
|
PERSONALITY_STANDARD = log.LongitudinalPersonality.schema.enumerants["standard"]
|
||||||
@@ -60,6 +61,20 @@ def apply_longitudinal_mode(params, mode: int) -> None:
|
|||||||
raise ValueError(f"invalid longitudinal mode: {mode}")
|
raise ValueError(f"invalid longitudinal mode: {mode}")
|
||||||
|
|
||||||
|
|
||||||
|
def longitudinal_mode_needs_cycle(previous: int, mode: int) -> bool:
|
||||||
|
return (previous == LONGITUDINAL_MODE_STOCK) != (mode == LONGITUDINAL_MODE_STOCK)
|
||||||
|
|
||||||
|
|
||||||
|
def next_longitudinal_mode(current: int, onroad: bool, iq_modes_available: bool) -> int:
|
||||||
|
if onroad:
|
||||||
|
order = list(IQ_LONGITUDINAL_MODES)
|
||||||
|
else:
|
||||||
|
order = [LONGITUDINAL_MODE_STOCK] + (list(IQ_LONGITUDINAL_MODES) if iq_modes_available else [])
|
||||||
|
if current not in order:
|
||||||
|
return current if onroad else order[0]
|
||||||
|
return order[(order.index(current) + 1) % len(order)]
|
||||||
|
|
||||||
|
|
||||||
def get_follow_distance_state(params) -> tuple[int | None, bool]:
|
def get_follow_distance_state(params) -> tuple[int | None, bool]:
|
||||||
mode = get_longitudinal_mode(params)
|
mode = get_longitudinal_mode(params)
|
||||||
if mode == LONGITUDINAL_MODE_STOCK:
|
if mode == LONGITUDINAL_MODE_STOCK:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import pytest
|
|||||||
from iqpilot.selfdrive.longitudinal_settings import (
|
from iqpilot.selfdrive.longitudinal_settings import (
|
||||||
LONGITUDINAL_MODE_CHILL,
|
LONGITUDINAL_MODE_CHILL,
|
||||||
LONGITUDINAL_MODE_DYNAMIC,
|
LONGITUDINAL_MODE_DYNAMIC,
|
||||||
|
LONGITUDINAL_MODE_PILOT,
|
||||||
LONGITUDINAL_MODE_STOCK,
|
LONGITUDINAL_MODE_STOCK,
|
||||||
PERSONALITY_AGGRESSIVE,
|
PERSONALITY_AGGRESSIVE,
|
||||||
PERSONALITY_RELAXED,
|
PERSONALITY_RELAXED,
|
||||||
@@ -16,6 +17,8 @@ from iqpilot.selfdrive.longitudinal_settings import (
|
|||||||
get_follow_distance_state,
|
get_follow_distance_state,
|
||||||
get_longitudinal_mode,
|
get_longitudinal_mode,
|
||||||
get_runtime_personality,
|
get_runtime_personality,
|
||||||
|
longitudinal_mode_needs_cycle,
|
||||||
|
next_longitudinal_mode,
|
||||||
set_valid_personality,
|
set_valid_personality,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -108,3 +111,31 @@ def test_dynamic_and_pilot_enable_valid_personality_selection():
|
|||||||
params.values["IQDynamicMode"] = False
|
params.values["IQDynamicMode"] = False
|
||||||
|
|
||||||
assert get_follow_distance_state(params) == (PERSONALITY_STANDARD, True)
|
assert get_follow_distance_state(params) == (PERSONALITY_STANDARD, True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_onroad_cycles_only_between_iq_modes():
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_CHILL, True, True) == LONGITUDINAL_MODE_DYNAMIC
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_DYNAMIC, True, True) == LONGITUDINAL_MODE_PILOT
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_PILOT, True, True) == LONGITUDINAL_MODE_CHILL
|
||||||
|
|
||||||
|
|
||||||
|
def test_onroad_stock_acc_is_locked():
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_STOCK, True, True) == LONGITUDINAL_MODE_STOCK
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_STOCK, True, False) == LONGITUDINAL_MODE_STOCK
|
||||||
|
|
||||||
|
|
||||||
|
def test_offroad_cycles_through_stock_acc():
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_PILOT, False, True) == LONGITUDINAL_MODE_STOCK
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_STOCK, False, True) == LONGITUDINAL_MODE_CHILL
|
||||||
|
|
||||||
|
|
||||||
|
def test_offroad_without_iq_modes_only_offers_stock_acc():
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_STOCK, False, False) == LONGITUDINAL_MODE_STOCK
|
||||||
|
assert next_longitudinal_mode(LONGITUDINAL_MODE_PILOT, False, False) == LONGITUDINAL_MODE_STOCK
|
||||||
|
|
||||||
|
|
||||||
|
def test_cycle_only_when_crossing_stock_boundary():
|
||||||
|
assert longitudinal_mode_needs_cycle(LONGITUDINAL_MODE_STOCK, LONGITUDINAL_MODE_CHILL)
|
||||||
|
assert longitudinal_mode_needs_cycle(LONGITUDINAL_MODE_PILOT, LONGITUDINAL_MODE_STOCK)
|
||||||
|
assert not longitudinal_mode_needs_cycle(LONGITUDINAL_MODE_CHILL, LONGITUDINAL_MODE_PILOT)
|
||||||
|
assert not longitudinal_mode_needs_cycle(LONGITUDINAL_MODE_DYNAMIC, LONGITUDINAL_MODE_CHILL)
|
||||||
|
|||||||
@@ -6,13 +6,17 @@ from iqpilot.common.params import Params, UnknownKeyName
|
|||||||
from iqpilot.selfdrive.longitudinal_settings import (
|
from iqpilot.selfdrive.longitudinal_settings import (
|
||||||
LONGITUDINAL_MODE_DYNAMIC,
|
LONGITUDINAL_MODE_DYNAMIC,
|
||||||
LONGITUDINAL_MODE_PILOT,
|
LONGITUDINAL_MODE_PILOT,
|
||||||
|
LONGITUDINAL_MODE_STOCK,
|
||||||
PERSONALITY_VALUES,
|
PERSONALITY_VALUES,
|
||||||
apply_longitudinal_mode,
|
apply_longitudinal_mode,
|
||||||
get_follow_distance_state,
|
get_follow_distance_state,
|
||||||
get_longitudinal_mode,
|
get_longitudinal_mode,
|
||||||
|
longitudinal_mode_needs_cycle,
|
||||||
|
next_longitudinal_mode,
|
||||||
set_valid_personality,
|
set_valid_personality,
|
||||||
)
|
)
|
||||||
from iqpilot.selfdrive.ui.mici.widgets.stock_button import BigButton, BigMultiToggle, BigToggle, BigParamControl
|
from iqpilot.selfdrive.ui.mici.widgets.stock_button import BigButton, BigMultiToggle, BigToggle, BigParamControl
|
||||||
|
from iqpilot.selfdrive.ui.ui_state import ui_state
|
||||||
from iqpilot.system.ui.lib.multilang import tr
|
from iqpilot.system.ui.lib.multilang import tr
|
||||||
|
|
||||||
|
|
||||||
@@ -138,24 +142,48 @@ class IQModeSelector(BigMultiToggle):
|
|||||||
super().__init__(tr("IQ Mode"), self._display_options)
|
super().__init__(tr("IQ Mode"), self._display_options)
|
||||||
self._params = Params()
|
self._params = Params()
|
||||||
self._mode_callback = mode_callback
|
self._mode_callback = mode_callback
|
||||||
|
self._mode = LONGITUDINAL_MODE_STOCK
|
||||||
|
self._iq_modes_available = False
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
self.set_enabled(lambda: self._next() != self._mode)
|
||||||
|
|
||||||
def _index(self) -> int:
|
def _index(self) -> int:
|
||||||
return get_longitudinal_mode(self._params)
|
return get_longitudinal_mode(self._params)
|
||||||
|
|
||||||
def is_dynamic(self) -> bool:
|
def is_dynamic(self) -> bool:
|
||||||
return self._index() == 2
|
return self._mode == LONGITUDINAL_MODE_DYNAMIC
|
||||||
|
|
||||||
|
def _toyota_factory_long_forced(self) -> bool:
|
||||||
|
cp = ui_state.CP
|
||||||
|
return bool(cp is not None and cp.brand == "toyota" and self._params.get_bool("IQToyotaFactoryLong"))
|
||||||
|
|
||||||
|
def _read_iq_modes_available(self) -> bool:
|
||||||
|
cp = ui_state.CP
|
||||||
|
alpha_available = bool(cp is not None and cp.alphaLongitudinalAvailable)
|
||||||
|
return alpha_available or self._params.get_bool("AlphaLongitudinalEnabled") or self._toyota_factory_long_forced()
|
||||||
|
|
||||||
|
def _next(self) -> int:
|
||||||
|
return next_longitudinal_mode(self._mode, ui_state.is_onroad(), self._iq_modes_available)
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.set_value(self._display_options[self._index()])
|
self._mode = self._index()
|
||||||
|
self._iq_modes_available = self._read_iq_modes_available()
|
||||||
|
self.set_value(self._display_options[self._mode])
|
||||||
|
|
||||||
def _apply(self, idx: int):
|
def _apply(self, idx: int):
|
||||||
|
previous = self._mode
|
||||||
|
toyota_forced = self._toyota_factory_long_forced()
|
||||||
apply_longitudinal_mode(self._params, idx)
|
apply_longitudinal_mode(self._params, idx)
|
||||||
self._params.put_bool("OnroadCycleRequested", True)
|
if idx != LONGITUDINAL_MODE_STOCK and toyota_forced:
|
||||||
|
self._params.put_bool("IQToyotaFactoryLong", False)
|
||||||
|
if longitudinal_mode_needs_cycle(previous, idx) or (idx != LONGITUDINAL_MODE_STOCK and toyota_forced):
|
||||||
|
self._params.put_bool("OnroadCycleRequested", True)
|
||||||
|
|
||||||
def _handle_mouse_release(self, mouse_pos):
|
def _handle_mouse_release(self, mouse_pos):
|
||||||
nxt = (self._index() + 1) % len(self.OPTIONS)
|
nxt = self._next()
|
||||||
|
if nxt == self._mode:
|
||||||
|
return
|
||||||
self._apply(nxt)
|
self._apply(nxt)
|
||||||
self.set_value(self._display_options[nxt])
|
self.refresh()
|
||||||
if self._mode_callback:
|
if self._mode_callback:
|
||||||
self._mode_callback()
|
self._mode_callback()
|
||||||
|
|||||||
@@ -128,6 +128,8 @@ class ModelsLayoutMici(NavScroller):
|
|||||||
self._big = BigButton(tr("big model"))
|
self._big = BigButton(tr("big model"))
|
||||||
self._big.set_click_callback(self._show_big_models)
|
self._big.set_click_callback(self._show_big_models)
|
||||||
|
|
||||||
|
self._small_on_mac = BigParamControl(tr("active model on eMac"), "IQEmacSmallModel", toggle_callback=self._small_on_mac_toggled)
|
||||||
|
|
||||||
self._cancel = BigButton(tr("stop download"))
|
self._cancel = BigButton(tr("stop download"))
|
||||||
self._cancel.set_click_callback(self._cancel_model_request)
|
self._cancel.set_click_callback(self._cancel_model_request)
|
||||||
self._cancel.set_visible(self._is_downloading)
|
self._cancel.set_visible(self._is_downloading)
|
||||||
@@ -158,7 +160,8 @@ class ModelsLayoutMici(NavScroller):
|
|||||||
self._lane_speed = MappedParamToggle(tr("lane turn speed"), "IQLaneTurnValue", [tr("slow"), tr("normal"), tr("fast")], _LANE_TURN_VALUES)
|
self._lane_speed = MappedParamToggle(tr("lane turn speed"), "IQLaneTurnValue", [tr("slow"), tr("normal"), tr("fast")], _LANE_TURN_VALUES)
|
||||||
self._lane_speed.set_visible(lambda: self._lane_turn._checked)
|
self._lane_speed.set_visible(lambda: self._lane_turn._checked)
|
||||||
|
|
||||||
self._main_items = [self._current, self._big, self._cancel, self._supercombo, self._vision, self._policy, self._redownload, self._refresh, self._clear,
|
self._main_items = [self._current, self._big, self._small_on_mac, self._cancel, self._supercombo, self._vision, self._policy,
|
||||||
|
self._redownload, self._refresh, self._clear,
|
||||||
self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed]
|
self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed]
|
||||||
self._scroller.add_widgets(self._main_items)
|
self._scroller.add_widgets(self._main_items)
|
||||||
|
|
||||||
@@ -394,9 +397,28 @@ class ModelsLayoutMici(NavScroller):
|
|||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _small_on_mac_toggled(self, checked: bool) -> None:
|
||||||
|
p = ui_state.params
|
||||||
|
if checked:
|
||||||
|
p.put_bool("IQEmacEnabled", True)
|
||||||
|
else:
|
||||||
|
p.put_bool("IQEmacEnabled", bool(p.get("IQEmacModel")))
|
||||||
|
|
||||||
|
def _small_on_mac_value(self) -> str:
|
||||||
|
try:
|
||||||
|
active = self.model_manager.activeBundle
|
||||||
|
name = _display_model_name(active) if active and active.ref else ""
|
||||||
|
except Exception:
|
||||||
|
name = ""
|
||||||
|
return f"{name} ({tr('eMac')})" if name else tr("active model")
|
||||||
|
|
||||||
def _big_model_value(self) -> str:
|
def _big_model_value(self) -> str:
|
||||||
p = ui_state.params
|
p = ui_state.params
|
||||||
dock = bool(getattr(ui_state.sm["deviceState"], "egpuDockPresent", False))
|
dock = bool(getattr(ui_state.sm["deviceState"], "egpuDockPresent", False))
|
||||||
|
if p.get_bool("IQEmacEnabled") and p.get_bool("IQEmacSmallModel"):
|
||||||
|
progress = self._big_setup_progress()
|
||||||
|
value = self._small_on_mac_value()
|
||||||
|
return f"{value} {int(progress * 100)}%" if progress is not None and progress < 1.0 else value
|
||||||
if not p.get_bool("IQEmacEnabled") and not dock:
|
if not p.get_bool("IQEmacEnabled") and not dock:
|
||||||
return tr("Off")
|
return tr("Off")
|
||||||
key = p.get("IQEmacModel")
|
key = p.get("IQEmacModel")
|
||||||
@@ -577,5 +599,5 @@ class ModelsLayoutMici(NavScroller):
|
|||||||
|
|
||||||
def show_event(self):
|
def show_event(self):
|
||||||
super().show_event()
|
super().show_event()
|
||||||
for w in (self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed):
|
for w in (self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed, self._small_on_mac):
|
||||||
w.refresh()
|
w.refresh()
|
||||||
|
|||||||
0
iqpilot/system/android/__init__.py
Normal file
0
iqpilot/system/android/__init__.py
Normal file
200
iqpilot/system/android/androidd.py
Normal file
200
iqpilot/system/android/androidd.py
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
|
from iqpilot.cereal import messaging
|
||||||
|
from iqpilot.common.params import Params
|
||||||
|
from iqpilot.common.realtime import Ratekeeper
|
||||||
|
from iqpilot.common.swaglog import cloudlog
|
||||||
|
|
||||||
|
ANDROID_ROOT = "/data/android"
|
||||||
|
HEADLESS = f"{ANDROID_ROOT}/waydroid_headless.sh"
|
||||||
|
TRIM = f"{ANDROID_ROOT}/android_trim.sh"
|
||||||
|
GUARD = f"{ANDROID_ROOT}/android_guard.sh"
|
||||||
|
LXC_ATTACH = f"{ANDROID_ROOT}/root/usr/bin/lxc-attach"
|
||||||
|
LD = f"{ANDROID_ROOT}/root/usr/lib/aarch64-linux-gnu"
|
||||||
|
|
||||||
|
# Waze's launcher is FreeMapAppActivity; there is no com.waze.MainActivity, and asking for
|
||||||
|
# one fails with "Activity class does not exist" rather than anything that names the problem.
|
||||||
|
NAV_APPS = {
|
||||||
|
"waze": ("com.waze", "com.waze/.FreeMapAppActivity"),
|
||||||
|
"maps": ("com.google.android.apps.maps", "com.google.android.apps.maps/com.google.android.maps.MapsActivity"),
|
||||||
|
}
|
||||||
|
BRIDGE_HOST = "192.168.240.112"
|
||||||
|
BRIDGE_PORT = 8099
|
||||||
|
GPS_SOURCES = ["iqLiveLocation", "liveLocationKalman", "gpsLocationExternal", "gpsLocation"]
|
||||||
|
PROVIDERS = ("gps", "fused", "network")
|
||||||
|
GPS_INTERVAL_S = 0.5
|
||||||
|
|
||||||
|
|
||||||
|
def attach(*args: str, timeout: float = 10.0) -> subprocess.CompletedProcess:
|
||||||
|
cmd = ["sudo", "-n", "env", f"LD_LIBRARY_PATH={LD}", LXC_ATTACH,
|
||||||
|
"-P", "/var/lib/waydroid/lxc", "-n", "waydroid", "--", *args]
|
||||||
|
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def container_booted() -> bool:
|
||||||
|
try:
|
||||||
|
return attach("/system/bin/getprop", "sys.boot_completed", timeout=8).stdout.strip() == "1"
|
||||||
|
except (subprocess.SubprocessError, OSError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def bring_up() -> None:
|
||||||
|
# AGNOS mounts / read-only, so no unit file can be installed; a transient unit is the only
|
||||||
|
# way the compositor and lxc-start survive this process exiting.
|
||||||
|
subprocess.run(["sudo", "-n", "systemd-run", "--unit=iq-android", "--service-type=oneshot",
|
||||||
|
"--remain-after-exit", HEADLESS, "up"],
|
||||||
|
capture_output=True, text=True, timeout=120, check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def start_guard() -> None:
|
||||||
|
subprocess.run(["sudo", "-n", "systemd-run", "--unit=iq-android-guard", GUARD],
|
||||||
|
capture_output=True, text=True, timeout=30, check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def trim(nav_app: str) -> None:
|
||||||
|
subprocess.run(["sudo", "-n", "env", f"ANDROID_NAV_APP={nav_app}", TRIM],
|
||||||
|
capture_output=True, timeout=240, check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def enable_mock_location() -> None:
|
||||||
|
# Wiped by every container restart, and the package form does not cover uid 0: without the
|
||||||
|
# --uid form every injection fails with SecurityException and the apps just say "no GPS".
|
||||||
|
attach("/system/bin/cmd", "appops", "set", "--uid", "0", "android:mock_location", "allow")
|
||||||
|
for p in PROVIDERS:
|
||||||
|
attach("/system/bin/cmd", "location", "providers", "add-test-provider", p)
|
||||||
|
attach("/system/bin/cmd", "location", "providers", "set-test-provider-enabled", p, "true")
|
||||||
|
# The a11y tree is only updated while the display is awake; asleep it emits nothing at all.
|
||||||
|
attach("/system/bin/svc", "power", "stayon", "true")
|
||||||
|
|
||||||
|
|
||||||
|
def inject(lat: float, lon: float) -> None:
|
||||||
|
for p in PROVIDERS:
|
||||||
|
attach("/system/bin/cmd", "location", "providers", "set-test-provider-location", p,
|
||||||
|
"--location", f"{lat:.6f},{lon:.6f}", "--accuracy", "4", timeout=6)
|
||||||
|
|
||||||
|
|
||||||
|
def read_position(sm: messaging.SubMaster) -> tuple[float, float] | None:
|
||||||
|
for src in GPS_SOURCES:
|
||||||
|
if src not in sm.data or not sm.valid.get(src, False):
|
||||||
|
continue
|
||||||
|
msg = sm[src]
|
||||||
|
for lat_a, lon_a in (("latitude", "longitude"), ("lat", "lon")):
|
||||||
|
lat = getattr(msg, lat_a, None)
|
||||||
|
lon = getattr(msg, lon_a, None)
|
||||||
|
if lat is not None and lon is not None and (lat or lon):
|
||||||
|
return float(lat), float(lon)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class AndroidNavDaemon:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.params = Params()
|
||||||
|
self.sources = [s for s in GPS_SOURCES if s in messaging.SERVICE_LIST]
|
||||||
|
self.sm = messaging.SubMaster(self.sources) if self.sources else None
|
||||||
|
self.last_inject = 0.0
|
||||||
|
self.up = False
|
||||||
|
|
||||||
|
def status(self, text: str) -> None:
|
||||||
|
self.params.put("IQAndroidNavStatus", text)
|
||||||
|
|
||||||
|
def sign_in(self) -> None:
|
||||||
|
email = self.params.get("IQAndroidNavEmail", encoding="utf8")
|
||||||
|
password = self.params.get("IQAndroidNavPassword", encoding="utf8")
|
||||||
|
if not email or not password:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with socket.create_connection((BRIDGE_HOST, BRIDGE_PORT), timeout=10) as s:
|
||||||
|
s.sendall(json.dumps({"cmd": "signin", "email": email, "password": password}).encode() + b"\n")
|
||||||
|
self.status("sign-in requested")
|
||||||
|
except OSError:
|
||||||
|
cloudlog.exception("androidd: sign-in request failed")
|
||||||
|
self.status("sign-in failed")
|
||||||
|
finally:
|
||||||
|
# Never let the credential outlive the one use it was handed over for.
|
||||||
|
self.params.remove("IQAndroidNavEmail")
|
||||||
|
self.params.remove("IQAndroidNavPassword")
|
||||||
|
|
||||||
|
def nav_app(self) -> str:
|
||||||
|
value = self.params.get("IQAndroidNavApp", encoding="utf8") or "waze"
|
||||||
|
return value if value in NAV_APPS else "waze"
|
||||||
|
|
||||||
|
def enforce_single_app(self, nav_app: str) -> None:
|
||||||
|
for name, (pkg, _) in NAV_APPS.items():
|
||||||
|
if name != nav_app:
|
||||||
|
attach("/system/bin/am", "force-stop", pkg)
|
||||||
|
|
||||||
|
def ensure_app_running(self, nav_app: str) -> None:
|
||||||
|
pkg, component = NAV_APPS[nav_app]
|
||||||
|
if attach("/system/bin/pidof", pkg, timeout=8).stdout.strip():
|
||||||
|
return
|
||||||
|
attach("/system/bin/am", "start", "-n", component, timeout=20)
|
||||||
|
|
||||||
|
def ensure_up(self) -> None:
|
||||||
|
if container_booted():
|
||||||
|
if not self.up:
|
||||||
|
nav_app = self.nav_app()
|
||||||
|
enable_mock_location()
|
||||||
|
trim(nav_app)
|
||||||
|
self.enforce_single_app(nav_app)
|
||||||
|
start_guard()
|
||||||
|
self.status(f"running:{nav_app}")
|
||||||
|
self.up = True
|
||||||
|
return
|
||||||
|
self.up = False
|
||||||
|
self.status("starting")
|
||||||
|
bring_up()
|
||||||
|
|
||||||
|
def step(self) -> None:
|
||||||
|
if not self.params.get_bool("IQAndroidNav"):
|
||||||
|
if self.up:
|
||||||
|
subprocess.run(["sudo", "-n", "systemctl", "stop", "iq-android", "iq-android-guard"],
|
||||||
|
capture_output=True, timeout=60, check=False)
|
||||||
|
self.up = False
|
||||||
|
self.status("disabled")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.ensure_up()
|
||||||
|
if not self.up:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.sign_in()
|
||||||
|
self.ensure_app_running(self.nav_app())
|
||||||
|
|
||||||
|
if self.sm is None:
|
||||||
|
return
|
||||||
|
self.sm.update(0)
|
||||||
|
now = time.monotonic()
|
||||||
|
if now - self.last_inject < GPS_INTERVAL_S:
|
||||||
|
return
|
||||||
|
position = read_position(self.sm)
|
||||||
|
if position is not None:
|
||||||
|
self.last_inject = now
|
||||||
|
try:
|
||||||
|
inject(*position)
|
||||||
|
except subprocess.SubprocessError:
|
||||||
|
cloudlog.exception("androidd: location inject failed")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
if not os.path.exists(HEADLESS):
|
||||||
|
cloudlog.warning("androidd: android stack not staged, exiting")
|
||||||
|
return
|
||||||
|
daemon = AndroidNavDaemon()
|
||||||
|
rk = Ratekeeper(2.0, print_delay_threshold=None)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
daemon.step()
|
||||||
|
except Exception:
|
||||||
|
cloudlog.exception("androidd: step failed")
|
||||||
|
rk.keep_time()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -67,6 +67,9 @@ def qcomgps(started: bool, params: Params, CP: car.CarParams) -> bool:
|
|||||||
def always_run(started: bool, params: Params, CP: car.CarParams) -> bool:
|
def always_run(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def android_nav(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||||
|
return params.get_bool("IQAndroidNav")
|
||||||
|
|
||||||
def only_onroad(started: bool, params: Params, CP: car.CarParams) -> bool:
|
def only_onroad(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||||
return started
|
return started
|
||||||
|
|
||||||
@@ -176,6 +179,7 @@ procs = [
|
|||||||
PythonProcess("journald", "iqpilot.system.journald", only_onroad, platform.system() != "Darwin"),
|
PythonProcess("journald", "iqpilot.system.journald", only_onroad, platform.system() != "Darwin"),
|
||||||
PythonProcess("micd", "iqpilot.system.micd", or_(iscar, livestream)),
|
PythonProcess("micd", "iqpilot.system.micd", or_(iscar, livestream)),
|
||||||
PythonProcess("timed", "iqpilot.system.timed", always_run, enabled=not PC),
|
PythonProcess("timed", "iqpilot.system.timed", always_run, enabled=not PC),
|
||||||
|
PythonProcess("androidd", "iqpilot.system.android.androidd", android_nav, enabled=TICI, restart_if_crash=True),
|
||||||
|
|
||||||
PythonProcess("dmonitoringmodeld", "iqpilot.selfdrive.dmonitoringmodeld.dmonitoringmodeld", driver_monitoring, enabled=not PC),
|
PythonProcess("dmonitoringmodeld", "iqpilot.selfdrive.dmonitoringmodeld.dmonitoringmodeld", driver_monitoring, enabled=not PC),
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
venv_site_packages = os.path.join(Dir("#").abspath, ".venv", "lib", "python3.12", "site-packages")
|
|
||||||
if os.path.isdir(venv_site_packages) and venv_site_packages not in sys.path:
|
|
||||||
sys.path.insert(0, venv_site_packages)
|
|
||||||
|
|
||||||
import imgui
|
import imgui
|
||||||
import iqdbc
|
import iqdbc
|
||||||
import libusb
|
import libusb
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ void ReplayStream::mergeSegments() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ReplayStream::loadRoute(const std::string &route, const std::string &data_dir, uint32_t replay_flags, bool auto_source) {
|
bool ReplayStream::loadRoute(const std::string &route, const std::string &data_dir, uint32_t replay_flags, bool auto_source) {
|
||||||
replay.reset(new Replay(route, {"can", "narrowRoadEncodeIdx", "cabinEncodeIdx", "wideRoadEncodeIdx", "carParams"},
|
replay.reset(new Replay(route, {"can", "roadEncodeIdx", "driverEncodeIdx", "wideRoadEncodeIdx", "carParams"},
|
||||||
{}, nullptr, replay_flags, data_dir, auto_source));
|
{}, nullptr, replay_flags, data_dir, auto_source));
|
||||||
replay->setSegmentCacheLimit(settings.max_cached_minutes);
|
replay->setSegmentCacheLimit(settings.max_cached_minutes);
|
||||||
replay->installEventFilter([this](const Event *event) { return eventFilter(event); });
|
replay->installEventFilter([this](const Event *event) { return eventFilter(event); });
|
||||||
|
|||||||
@@ -90,3 +90,16 @@ class TestFrontendRemoval:
|
|||||||
def test_imgui_frontend_is_present(self):
|
def test_imgui_frontend_is_present(self):
|
||||||
assert (CABANA_DIR / "ui" / "app.cc").is_file()
|
assert (CABANA_DIR / "ui" / "app.cc").is_file()
|
||||||
assert (CABANA_DIR / "ui" / "main.cc").is_file()
|
assert (CABANA_DIR / "ui" / "main.cc").is_file()
|
||||||
|
|
||||||
|
def test_stream_selector_stays_in_main_window(self):
|
||||||
|
app = read("ui/app.cc")
|
||||||
|
assert "io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable" not in app
|
||||||
|
|
||||||
|
|
||||||
|
class TestReplayVideo:
|
||||||
|
def test_iqpilot_camera_index_services_are_replayed(self):
|
||||||
|
source = read("streams/replaystream.cc")
|
||||||
|
for service in ("roadEncodeIdx", "driverEncodeIdx", "wideRoadEncodeIdx"):
|
||||||
|
assert f'"{service}"' in source
|
||||||
|
assert '"narrowRoadEncodeIdx"' not in source
|
||||||
|
assert '"cabinEncodeIdx"' not in source
|
||||||
|
|||||||
@@ -144,8 +144,6 @@ public:
|
|||||||
ImPlot::CreateContext();
|
ImPlot::CreateContext();
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
|
||||||
io.ConfigViewportsNoDecoration = false;
|
|
||||||
io.IniFilename = nullptr;
|
io.IniFilename = nullptr;
|
||||||
io.LogFilename = nullptr;
|
io.LogFilename = nullptr;
|
||||||
if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {
|
if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {
|
||||||
|
|||||||
@@ -15,19 +15,19 @@
|
|||||||
#include "tools/cabana/utils/util.h"
|
#include "tools/cabana/utils/util.h"
|
||||||
|
|
||||||
void OpenReplayWidget::draw() {
|
void OpenReplayWidget::draw() {
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::TextDisabled("Replay a local or Konn3kt route with optional camera streams.");
|
||||||
|
ImGui::Spacing();
|
||||||
ImGui::TextUnformatted("Route");
|
ImGui::TextUnformatted("Route");
|
||||||
ImGui::SameLine();
|
ImGui::SetNextItemWidth(-1.0f);
|
||||||
ImGui::SetNextItemWidth(-250.0f);
|
|
||||||
inputText("##route", &route_, "Enter route name or browse for local/remote route");
|
inputText("##route", &route_, "Enter route name or browse for local/remote route");
|
||||||
ImGui::SameLine();
|
const float route_button_width = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||||
if (ImGui::Button("Remote route...")) {
|
if (ImGui::Button("Browse Konn3kt routes", ImVec2(route_button_width, 34.0f))) {
|
||||||
routes_dialog_.open(utils::guarded(alive_, [this](bool accepted, const std::string &route) {
|
routes_dialog_.open(utils::guarded(alive_, [this](bool accepted, const std::string &route) {
|
||||||
if (accepted) route_ = route;
|
if (accepted) route_ = route;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Local route...")) {
|
if (ImGui::Button("Choose local route", ImVec2(-1.0f, 34.0f))) {
|
||||||
FileDialog::getExistingDirectory("Open Local Route", settings.last_route_dir, utils::guarded(alive_, [this](const std::string &dir) {
|
FileDialog::getExistingDirectory("Open Local Route", settings.last_route_dir, utils::guarded(alive_, [this](const std::string &dir) {
|
||||||
if (!dir.empty()) {
|
if (!dir.empty()) {
|
||||||
route_ = dir;
|
route_ = dir;
|
||||||
@@ -35,6 +35,8 @@ void OpenReplayWidget::draw() {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::SeparatorText("Camera streams");
|
||||||
checkBox("Road camera", &cameras_[0]);
|
checkBox("Road camera", &cameras_[0]);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
checkBox("Driver camera", &cameras_[1]);
|
checkBox("Driver camera", &cameras_[1]);
|
||||||
@@ -126,6 +128,8 @@ void OpenPandaWidget::buildConfigForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenPandaWidget::draw() {
|
void OpenPandaWidget::draw() {
|
||||||
|
ImGui::TextDisabled("Connect directly to a Panda and configure each CAN bus.");
|
||||||
|
ImGui::Spacing();
|
||||||
if (already_connected_) {
|
if (already_connected_) {
|
||||||
ImGui::Text("Already connected to %s.", can->routeName().c_str());
|
ImGui::Text("Already connected to %s.", can->routeName().c_str());
|
||||||
ImGui::TextUnformatted("Close the current connection via [File menu -> Close Stream] before connecting to another Panda.");
|
ImGui::TextUnformatted("Close the current connection via [File menu -> Close Stream] before connecting to another Panda.");
|
||||||
@@ -184,6 +188,8 @@ std::unique_ptr<AbstractStream> OpenPandaWidget::open() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenDeviceWidget::draw() {
|
void OpenDeviceWidget::draw() {
|
||||||
|
ImGui::TextDisabled("Connect to a running IQ.Pilot instance or a local message queue.");
|
||||||
|
ImGui::Spacing();
|
||||||
ImGui::RadioButton("MSGQ", &mode_, 0);
|
ImGui::RadioButton("MSGQ", &mode_, 0);
|
||||||
ImGui::RadioButton("ZMQ", &mode_, 1);
|
ImGui::RadioButton("ZMQ", &mode_, 1);
|
||||||
ImGui::RadioButton("Bridge", &mode_, 2);
|
ImGui::RadioButton("Bridge", &mode_, 2);
|
||||||
@@ -226,6 +232,8 @@ void OpenSocketCanWidget::refreshDevices() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenSocketCanWidget::draw() {
|
void OpenSocketCanWidget::draw() {
|
||||||
|
ImGui::TextDisabled("Read CAN traffic from a Linux SocketCAN interface.");
|
||||||
|
ImGui::Spacing();
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::TextUnformatted("Device");
|
ImGui::TextUnformatted("Device");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -248,7 +256,6 @@ std::unique_ptr<AbstractStream> OpenSocketCanWidget::open() {
|
|||||||
void StreamSelector::open(Callback on_done) {
|
void StreamSelector::open(Callback on_done) {
|
||||||
on_done_ = std::move(on_done);
|
on_done_ = std::move(on_done);
|
||||||
open_ = true;
|
open_ = true;
|
||||||
popup_.reset();
|
|
||||||
first_frame_ = true;
|
first_frame_ = true;
|
||||||
dbc_file_.clear();
|
dbc_file_.clear();
|
||||||
widgets_.clear();
|
widgets_.clear();
|
||||||
@@ -264,32 +271,65 @@ void StreamSelector::open(Callback on_done) {
|
|||||||
|
|
||||||
void StreamSelector::draw() {
|
void StreamSelector::draw() {
|
||||||
if (!open_) return;
|
if (!open_) return;
|
||||||
if (!beginDialog("Open stream", &popup_, ImVec2(640.0f, 0.0f))) return;
|
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||||
|
ImGui::SetNextWindowPos(viewport->WorkPos);
|
||||||
|
ImGui::SetNextWindowSize(viewport->WorkSize);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
const ImGuiWindowFlags page_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||||
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking;
|
||||||
|
if (!ImGui::Begin("##stream_selector_page", nullptr, page_flags)) {
|
||||||
|
ImGui::End();
|
||||||
|
ImGui::PopStyleVar(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar(2);
|
||||||
|
|
||||||
|
const ImVec2 available = ImGui::GetContentRegionAvail();
|
||||||
|
const ImVec2 card_size(std::clamp(available.x - 80.0f, 680.0f, 920.0f),
|
||||||
|
std::clamp(available.y - 80.0f, 470.0f, 570.0f));
|
||||||
|
ImGui::SetCursorPos(ImVec2(std::max(24.0f, (available.x - card_size.x) * 0.5f),
|
||||||
|
std::max(24.0f, (available.y - card_size.y) * 0.5f)));
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 12.0f);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(28.0f, 24.0f));
|
||||||
|
ImGui::BeginChild("##stream_selector_card", card_size, ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollbar);
|
||||||
|
ImGui::PopStyleVar(3);
|
||||||
|
|
||||||
|
ImGui::PushFont(boldFont(), 26.0f);
|
||||||
|
ImGui::TextUnformatted("Open Cabana");
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::TextDisabled("Choose a data source to inspect CAN traffic, signals, and video.");
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
AbstractOpenStreamWidget *current = nullptr;
|
AbstractOpenStreamWidget *current = nullptr;
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(16.0f, 7.0f));
|
||||||
if (ImGui::BeginTabBar("streams")) {
|
if (ImGui::BeginTabBar("streams")) {
|
||||||
for (auto &w : widgets_) {
|
for (auto &w : widgets_) {
|
||||||
|
|
||||||
ImGuiTabItemFlags tab_flags = (first_frame_ && w == widgets_.front()) ? ImGuiTabItemFlags_SetSelected : 0;
|
ImGuiTabItemFlags tab_flags = (first_frame_ && w == widgets_.front()) ? ImGuiTabItemFlags_SetSelected : 0;
|
||||||
if (ImGui::BeginTabItem(w->title(), nullptr, tab_flags)) {
|
if (ImGui::BeginTabItem(w->title(), nullptr, tab_flags)) {
|
||||||
current = w.get();
|
current = w.get();
|
||||||
ImGui::BeginChild("tab", ImVec2(0, 130.0f));
|
const float content_height = std::max(170.0f, card_size.y - 300.0f);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(18.0f, 16.0f));
|
||||||
|
ImGui::BeginChild("tab", ImVec2(0, content_height), ImGuiChildFlags_Borders);
|
||||||
w->draw();
|
w->draw();
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndTabBar();
|
ImGui::EndTabBar();
|
||||||
}
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
first_frame_ = false;
|
first_frame_ = false;
|
||||||
|
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::SeparatorText("DBC file");
|
||||||
ImGui::TextUnformatted("dbc File");
|
ImGui::SetNextItemWidth(-120.0f);
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(-90.0f);
|
|
||||||
inputText("##dbc", &dbc_file_, "Choose a dbc file to open", ImGuiInputTextFlags_ReadOnly);
|
inputText("##dbc", &dbc_file_, "Choose a dbc file to open", ImGuiInputTextFlags_ReadOnly);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Browse...")) {
|
if (ImGui::Button("Browse...", ImVec2(-1.0f, 0.0f))) {
|
||||||
FileDialog::getOpenFileName("Open File", settings.last_dir, ".dbc", [this](const std::string &fn) {
|
FileDialog::getOpenFileName("Open File", settings.last_dir, ".dbc", [this](const std::string &fn) {
|
||||||
if (!fn.empty()) {
|
if (!fn.empty()) {
|
||||||
dbc_file_ = fn;
|
dbc_file_ = fn;
|
||||||
@@ -302,7 +342,13 @@ void StreamSelector::draw() {
|
|||||||
bool accepted = false, rejected = false;
|
bool accepted = false, rejected = false;
|
||||||
std::unique_ptr<AbstractStream> stream;
|
std::unique_ptr<AbstractStream> stream;
|
||||||
bool open_clicked = false;
|
bool open_clicked = false;
|
||||||
dialogButtons("Open", &open_clicked, &rejected, current != nullptr && current->openEnabled());
|
const float open_width = 180.0f;
|
||||||
|
if (ImGui::Button("Cancel", ImVec2(100.0f, 38.0f))) rejected = true;
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - open_width);
|
||||||
|
ImGui::BeginDisabled(current == nullptr || !current->openEnabled());
|
||||||
|
if (ImGui::Button("Open source", ImVec2(open_width, 38.0f))) open_clicked = true;
|
||||||
|
ImGui::EndDisabled();
|
||||||
if (open_clicked) {
|
if (open_clicked) {
|
||||||
if (stream = current->open(); stream) accepted = true;
|
if (stream = current->open(); stream) accepted = true;
|
||||||
}
|
}
|
||||||
@@ -312,8 +358,8 @@ void StreamSelector::draw() {
|
|||||||
FileDialog::draw();
|
FileDialog::draw();
|
||||||
MessageBox::draw();
|
MessageBox::draw();
|
||||||
|
|
||||||
if (accepted || rejected) ImGui::CloseCurrentPopup();
|
ImGui::EndChild();
|
||||||
ImGui::EndPopup();
|
ImGui::End();
|
||||||
if (accepted || rejected) {
|
if (accepted || rejected) {
|
||||||
open_ = false;
|
open_ = false;
|
||||||
widgets_.clear();
|
widgets_.clear();
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool open_ = false;
|
bool open_ = false;
|
||||||
PopupOwner popup_;
|
|
||||||
bool first_frame_ = false;
|
bool first_frame_ = false;
|
||||||
std::string dbc_file_;
|
std::string dbc_file_;
|
||||||
std::vector<std::unique_ptr<AbstractOpenStreamWidget>> widgets_;
|
std::vector<std::unique_ptr<AbstractOpenStreamWidget>> widgets_;
|
||||||
|
|||||||
@@ -44,6 +44,20 @@ def host():
|
|||||||
|
|
||||||
class TestFileDownload:
|
class TestFileDownload:
|
||||||
|
|
||||||
|
def test_head_connection_released(self, monkeypatch):
|
||||||
|
class Response:
|
||||||
|
status = 200
|
||||||
|
headers = {"content-length": "4"}
|
||||||
|
released = False
|
||||||
|
|
||||||
|
def release_conn(self):
|
||||||
|
self.released = True
|
||||||
|
|
||||||
|
response = Response()
|
||||||
|
monkeypatch.setattr(URLFile, "_request", lambda self, method, url, headers=None: response)
|
||||||
|
assert URLFile("https://example.com/test").get_length_online() == 4
|
||||||
|
assert response.released
|
||||||
|
|
||||||
def test_pipeline_defaults(self, host):
|
def test_pipeline_defaults(self, host):
|
||||||
# TODO: parameterize the defaults so we don't rely on hard-coded values in xx
|
# TODO: parameterize the defaults so we don't rely on hard-coded values in xx
|
||||||
|
|
||||||
|
|||||||
@@ -128,10 +128,13 @@ class URLFile:
|
|||||||
|
|
||||||
def get_length_online(self) -> int:
|
def get_length_online(self) -> int:
|
||||||
response = self._request('HEAD', self._url)
|
response = self._request('HEAD', self._url)
|
||||||
if not (200 <= response.status <= 299):
|
try:
|
||||||
return -1
|
if not (200 <= response.status <= 299):
|
||||||
length = response.headers.get('content-length', 0)
|
return -1
|
||||||
return int(length)
|
length = response.headers.get('content-length', 0)
|
||||||
|
return int(length)
|
||||||
|
finally:
|
||||||
|
response.release_conn()
|
||||||
|
|
||||||
def get_length(self) -> int:
|
def get_length(self) -> int:
|
||||||
if self._length is not None:
|
if self._length is not None:
|
||||||
|
|||||||
@@ -40,6 +40,36 @@ def write_rlog(path: Path, n_frames: int = 200):
|
|||||||
f.write(msg.to_bytes())
|
f.write(msg.to_bytes())
|
||||||
|
|
||||||
|
|
||||||
|
def write_video_rlog(path: Path, n_frames: int):
|
||||||
|
with open(path, "wb") as f:
|
||||||
|
cp = messaging.new_message('carParams')
|
||||||
|
cp.logMonoTime = 1_000_000_000
|
||||||
|
cp.carParams.carFingerprint = "TOYOTA_RAV4_TSS2"
|
||||||
|
cp.carParams.brand = "toyota"
|
||||||
|
f.write(cp.to_bytes())
|
||||||
|
|
||||||
|
for i in range(n_frames):
|
||||||
|
timestamp = 1_000_000_000 + i * 50_000_000
|
||||||
|
msg = messaging.new_message('can', 1)
|
||||||
|
msg.logMonoTime = timestamp
|
||||||
|
msg.can[0].address = 0x1D2
|
||||||
|
msg.can[0].src = 0
|
||||||
|
msg.can[0].dat = bytes([i % 256] * 8)
|
||||||
|
f.write(msg.to_bytes())
|
||||||
|
|
||||||
|
idx = messaging.new_message('roadEncodeIdx')
|
||||||
|
idx.logMonoTime = timestamp
|
||||||
|
idx.roadEncodeIdx.frameId = i
|
||||||
|
idx.roadEncodeIdx.type = 'fullHEVC'
|
||||||
|
idx.roadEncodeIdx.encodeId = i
|
||||||
|
idx.roadEncodeIdx.segmentNum = 0
|
||||||
|
idx.roadEncodeIdx.segmentId = i
|
||||||
|
idx.roadEncodeIdx.segmentIdEncode = i
|
||||||
|
idx.roadEncodeIdx.timestampSof = timestamp
|
||||||
|
idx.roadEncodeIdx.timestampEof = timestamp + 10_000_000
|
||||||
|
f.write(idx.to_bytes())
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def local_route(tmp_path_factory):
|
def local_route(tmp_path_factory):
|
||||||
data_dir = tmp_path_factory.mktemp("routes")
|
data_dir = tmp_path_factory.mktemp("routes")
|
||||||
@@ -50,6 +80,25 @@ def local_route(tmp_path_factory):
|
|||||||
return data_dir
|
return data_dir
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def local_video_route(tmp_path_factory):
|
||||||
|
data_dir = tmp_path_factory.mktemp("video_routes")
|
||||||
|
seg_dir = data_dir / f"{DONGLE_ID}|{TIMESTAMP}--0"
|
||||||
|
seg_dir.mkdir()
|
||||||
|
frame_count = 20
|
||||||
|
result = subprocess.run([
|
||||||
|
"ffmpeg", "-hide_banner", "-loglevel", "error", "-f", "lavfi",
|
||||||
|
"-i", "testsrc=size=320x180:rate=20", "-frames:v", str(frame_count),
|
||||||
|
"-pix_fmt", "yuv420p", "-c:v", "libx265", "-preset", "ultrafast",
|
||||||
|
"-x265-params", "pools=1:frame-threads=1:log-level=error", "-f", "hevc",
|
||||||
|
str(seg_dir / "fcamera.hevc"),
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
pytest.skip(result.stderr)
|
||||||
|
write_video_rlog(seg_dir / "rlog", frame_count)
|
||||||
|
return data_dir
|
||||||
|
|
||||||
|
|
||||||
def run(cmd, timeout=180):
|
def run(cmd, timeout=180):
|
||||||
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, env=os.environ.copy(),
|
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, env=os.environ.copy(),
|
||||||
cwd=TOOLS_DIR.parent)
|
cwd=TOOLS_DIR.parent)
|
||||||
@@ -62,6 +111,33 @@ def cabana_command(*args):
|
|||||||
return command
|
return command
|
||||||
|
|
||||||
|
|
||||||
|
def cabana_output_until(args, expected, timeout=60):
|
||||||
|
master, slave = pty.openpty()
|
||||||
|
proc = subprocess.Popen(cabana_command(*args), stdout=slave, stderr=slave,
|
||||||
|
env=os.environ.copy(), cwd=TOOLS_DIR.parent)
|
||||||
|
os.close(slave)
|
||||||
|
output = bytearray()
|
||||||
|
deadline = time.monotonic() + timeout
|
||||||
|
try:
|
||||||
|
while time.monotonic() < deadline:
|
||||||
|
ready, _, _ = select.select([master], [], [], min(1, deadline - time.monotonic()))
|
||||||
|
if not ready:
|
||||||
|
if proc.poll() is not None:
|
||||||
|
break
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
output.extend(os.read(master, 4096))
|
||||||
|
except OSError:
|
||||||
|
break
|
||||||
|
if expected.encode() in output:
|
||||||
|
break
|
||||||
|
finally:
|
||||||
|
proc.kill()
|
||||||
|
proc.wait()
|
||||||
|
os.close(master)
|
||||||
|
return output.decode(errors="replace")
|
||||||
|
|
||||||
|
|
||||||
def test_jotpluggler_renders_a_local_route(local_route, tmp_path):
|
def test_jotpluggler_renders_a_local_route(local_route, tmp_path):
|
||||||
assert JOTPLUGGLER_BIN.exists(), "jotpluggler not built"
|
assert JOTPLUGGLER_BIN.exists(), "jotpluggler not built"
|
||||||
out = tmp_path / "plot.png"
|
out = tmp_path / "plot.png"
|
||||||
@@ -74,38 +150,20 @@ def test_jotpluggler_renders_a_local_route(local_route, tmp_path):
|
|||||||
|
|
||||||
def test_cabana_loads_a_local_route(local_route):
|
def test_cabana_loads_a_local_route(local_route):
|
||||||
assert CABANA_BIN.exists(), "cabana not built"
|
assert CABANA_BIN.exists(), "cabana not built"
|
||||||
master, slave = pty.openpty()
|
|
||||||
proc = subprocess.Popen(cabana_command("--data_dir", str(local_route), "--no-vipc", ROUTE),
|
|
||||||
stdout=slave, stderr=slave,
|
|
||||||
env=os.environ.copy(), cwd=TOOLS_DIR.parent)
|
|
||||||
os.close(slave)
|
|
||||||
loaded = f"loaded route {ROUTE} with 2 valid segments"
|
loaded = f"loaded route {ROUTE} with 2 valid segments"
|
||||||
output = bytearray()
|
out = cabana_output_until(("--data_dir", str(local_route), "--no-vipc", ROUTE), loaded)
|
||||||
deadline = time.monotonic() + 60
|
|
||||||
try:
|
|
||||||
while time.monotonic() < deadline:
|
|
||||||
ready, _, _ = select.select([master], [], [], min(1, deadline - time.monotonic()))
|
|
||||||
if not ready:
|
|
||||||
if proc.poll() is not None:
|
|
||||||
break
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
output.extend(os.read(master, 4096))
|
|
||||||
except OSError:
|
|
||||||
break
|
|
||||||
if loaded.encode() in output:
|
|
||||||
break
|
|
||||||
finally:
|
|
||||||
proc.kill()
|
|
||||||
proc.wait()
|
|
||||||
os.close(master)
|
|
||||||
|
|
||||||
out = output.decode(errors="replace")
|
|
||||||
assert "failed to load route" not in out, out
|
assert "failed to load route" not in out, out
|
||||||
assert "invalid route format" not in out, out
|
assert "invalid route format" not in out, out
|
||||||
assert loaded in out, out
|
assert loaded in out, out
|
||||||
|
|
||||||
|
|
||||||
|
def test_cabana_replays_local_video(local_video_route):
|
||||||
|
expected = "camera[0] vipc send #1"
|
||||||
|
out = cabana_output_until(("--data_dir", str(local_video_route), ROUTE), expected)
|
||||||
|
assert "failed to get frame" not in out, out
|
||||||
|
assert expected in out, out
|
||||||
|
|
||||||
|
|
||||||
def test_replay_logreader_reports_load_stats(local_route):
|
def test_replay_logreader_reports_load_stats(local_route):
|
||||||
assert shutil.which("python3") is not None
|
assert shutil.which("python3") is not None
|
||||||
header = (TOOLS_DIR / "replay" / "logreader.h").read_text()
|
header = (TOOLS_DIR / "replay" / "logreader.h").read_text()
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ from iqpilot.system.ui.iqwidgets.widgets.list_view import (
|
|||||||
Spacer,
|
Spacer,
|
||||||
)
|
)
|
||||||
from iqpilot.system.ui.iqwidgets.widgets.list_view import IQListItem
|
from iqpilot.system.ui.iqwidgets.widgets.list_view import IQListItem
|
||||||
from iqpilot.system.ui.iqwidgets.widgets.list_view import IQListItem, IQMultipleButtonAction, IQToggleAction, IQLineSeparator
|
from iqpilot.system.ui.iqwidgets.widgets.list_view import IQListItem, IQMultipleButtonAction, IQToggleAction, IQLineSeparator, toggle_item_iq
|
||||||
from iqpilot.system.ui.iqwidgets.widgets.list_view import button_item, toggle_item
|
from iqpilot.system.ui.iqwidgets.widgets.list_view import button_item, toggle_item
|
||||||
from iqpilot.system.ui.iqwidgets.widgets.list_view import NoticeModal
|
from iqpilot.system.ui.iqwidgets.widgets.list_view import NoticeModal
|
||||||
from iqpilot.system.ui.iqwidgets.widgets.list_view import PickerDialog, PickerItem, PickerGroup
|
from iqpilot.system.ui.iqwidgets.widgets.list_view import PickerDialog, PickerItem, PickerGroup
|
||||||
@@ -1655,6 +1655,14 @@ class ModelsLayout(Widget):
|
|||||||
)
|
)
|
||||||
self.big_model_item.action_item.set_value(self._big_model_value())
|
self.big_model_item.action_item.set_value(self._big_model_value())
|
||||||
|
|
||||||
|
self.small_on_mac_item = toggle_item_iq(
|
||||||
|
lambda: tr("Active Model on eMac"),
|
||||||
|
tr("Run the selected small model on the Mac instead of a big model."),
|
||||||
|
initial_state=ui_state.params.get_bool("IQEmacSmallModel"),
|
||||||
|
callback=self._on_small_on_mac_toggled,
|
||||||
|
param="IQEmacSmallModel",
|
||||||
|
)
|
||||||
|
|
||||||
self.supercombo_label = progress_item(tr("Combined Model"))
|
self.supercombo_label = progress_item(tr("Combined Model"))
|
||||||
self.vision_label = progress_item(tr("Vision Weights"))
|
self.vision_label = progress_item(tr("Vision Weights"))
|
||||||
self.policy_label = progress_item(tr("Policy Weights"))
|
self.policy_label = progress_item(tr("Policy Weights"))
|
||||||
@@ -1671,7 +1679,7 @@ class ModelsLayout(Widget):
|
|||||||
self.redownload_item = button_item(lambda: tr("Redownload Current Model"), lambda: tr("REDOWNLOAD"), "", self._redownload_model)
|
self.redownload_item = button_item(lambda: tr("Redownload Current Model"), lambda: tr("REDOWNLOAD"), "", self._redownload_model)
|
||||||
self.cancel_download_item = button_item(tr("Stop Download"), tr("Cancel"), "", self._cancel_model_request)
|
self.cancel_download_item = button_item(tr("Stop Download"), tr("Cancel"), "", self._cancel_model_request)
|
||||||
|
|
||||||
self.items = [self.current_model_item, self.big_model_item, self.cancel_download_item, self.supercombo_label, self.vision_label,
|
self.items = [self.current_model_item, self.big_model_item, self.small_on_mac_item, self.cancel_download_item, self.supercombo_label, self.vision_label,
|
||||||
self.policy_label, self.redownload_item, self.refresh_item, self.clear_cache_item]
|
self.policy_label, self.redownload_item, self.refresh_item, self.clear_cache_item]
|
||||||
|
|
||||||
def _is_downloading(self):
|
def _is_downloading(self):
|
||||||
@@ -1921,10 +1929,18 @@ class ModelsLayout(Widget):
|
|||||||
get_folders_fn=self._get_folders, on_exit=self._on_model_selected)
|
get_folders_fn=self._get_folders, on_exit=self._on_model_selected)
|
||||||
gui_app.set_modal_overlay(self.model_dialog, callback=self._on_model_selected)
|
gui_app.set_modal_overlay(self.model_dialog, callback=self._on_model_selected)
|
||||||
|
|
||||||
|
def _on_small_on_mac_toggled(self, state: bool) -> None:
|
||||||
|
p = ui_state.params
|
||||||
|
p.put_bool("IQEmacSmallModel", bool(state))
|
||||||
|
p.put_bool("IQEmacEnabled", True if state else bool(p.get("IQEmacModel")))
|
||||||
|
self.big_model_item.action_item.set_value(self._big_model_value())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _big_model_value() -> str:
|
def _big_model_value() -> str:
|
||||||
if not ui_state.params.get_bool("IQEmacEnabled"):
|
if not ui_state.params.get_bool("IQEmacEnabled"):
|
||||||
return tr("Off")
|
return tr("Off")
|
||||||
|
if ui_state.params.get_bool("IQEmacSmallModel"):
|
||||||
|
return tr("Active model (eMac)")
|
||||||
key = ui_state.params.get("IQEmacModel")
|
key = ui_state.params.get("IQEmacModel")
|
||||||
key = key.decode() if isinstance(key, bytes) else (key or "")
|
key = key.decode() if isinstance(key, bytes) else (key or "")
|
||||||
return _big_model_label(key) if key in [n for n, _ in _big_model_options()] else tr("Off")
|
return _big_model_label(key) if key in [n for n, _ in _big_model_options()] else tr("Off")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import pyray as rl
|
import pyray as rl
|
||||||
@@ -17,6 +18,9 @@ from iqpilot.system.ui.widgets import Widget
|
|||||||
|
|
||||||
_POLL_S = 1.0
|
_POLL_S = 1.0
|
||||||
_FONT_SIZE = 70
|
_FONT_SIZE = 70
|
||||||
|
_ICON_H = 76
|
||||||
|
_ICONS = {"MAC": ("mac", 62 / 46), "GPU": ("egpu", 1.0)}
|
||||||
|
_GREY = rl.Color(165, 165, 170, 235)
|
||||||
|
|
||||||
|
|
||||||
class EmacStatusRenderer(Widget):
|
class EmacStatusRenderer(Widget):
|
||||||
@@ -27,6 +31,21 @@ class EmacStatusRenderer(Widget):
|
|||||||
self._last_poll = 0.0
|
self._last_poll = 0.0
|
||||||
self._label = ""
|
self._label = ""
|
||||||
self._state = SourceState.HIDDEN
|
self._state = SourceState.HIDDEN
|
||||||
|
self._icons: dict[str, dict[str, rl.Texture]] = {}
|
||||||
|
|
||||||
|
def _icon_set(self, label: str) -> dict[str, rl.Texture] | None:
|
||||||
|
spec = _ICONS.get(label)
|
||||||
|
if spec is None:
|
||||||
|
return None
|
||||||
|
if label not in self._icons:
|
||||||
|
base, aspect = spec
|
||||||
|
w = int(_ICON_H * aspect)
|
||||||
|
self._icons[label] = {
|
||||||
|
"base": gui_app.texture(f"icons_mici/{base}.png", w, _ICON_H),
|
||||||
|
"green": gui_app.texture(f"icons_mici/{base}_green.png", w, _ICON_H),
|
||||||
|
"orange": gui_app.texture(f"icons_mici/{base}_orange.png", int(w * 1.26), _ICON_H),
|
||||||
|
}
|
||||||
|
return self._icons[label]
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
@@ -38,7 +57,25 @@ class EmacStatusRenderer(Widget):
|
|||||||
def _render(self, rect: rl.Rectangle):
|
def _render(self, rect: rl.Rectangle):
|
||||||
if self._state == SourceState.HIDDEN:
|
if self._state == SourceState.HIDDEN:
|
||||||
return
|
return
|
||||||
size = measure_text_cached(self._font, self._label, _FONT_SIZE)
|
icons = self._icon_set(self._label)
|
||||||
x = rect.x + UI_BORDER_SIZE + BTN_SIZE // 2 - size.x / 2
|
if icons is None:
|
||||||
y = rect.y + rect.height / 2 - size.y / 2
|
size = measure_text_cached(self._font, self._label, _FONT_SIZE)
|
||||||
draw_source_label(self._font, self._label, self._state, rl.Vector2(x, y), _FONT_SIZE)
|
x = rect.x + UI_BORDER_SIZE + BTN_SIZE // 2 - size.x / 2
|
||||||
|
y = rect.y + rect.height / 2 - size.y / 2
|
||||||
|
draw_source_label(self._font, self._label, self._state, rl.Vector2(x, y), _FONT_SIZE)
|
||||||
|
return
|
||||||
|
if self._state == SourceState.ACTIVE:
|
||||||
|
tex, tint = icons["green"], rl.Color(255, 255, 255, 255)
|
||||||
|
elif self._state == SourceState.FAILED:
|
||||||
|
tex, tint = icons["orange"], rl.Color(255, 255, 255, 255)
|
||||||
|
elif self._state == SourceState.CROSSED:
|
||||||
|
tex, tint = icons["base"], rl.Color(255, 255, 255, 165)
|
||||||
|
else:
|
||||||
|
pulse = 0.35 + 0.65 * (0.5 - 0.5 * math.cos(rl.get_time() * 6.0))
|
||||||
|
tex, tint = icons["base"], rl.Color(_GREY.r, _GREY.g, _GREY.b, int(_GREY.a * pulse))
|
||||||
|
x = int(rect.x + UI_BORDER_SIZE + BTN_SIZE // 2 - tex.width / 2)
|
||||||
|
y = int(rect.y + rect.height / 2 - tex.height / 2)
|
||||||
|
rl.draw_texture(tex, x, y, tint)
|
||||||
|
if self._state == SourceState.CROSSED:
|
||||||
|
cy = y + tex.height // 2
|
||||||
|
rl.draw_line_ex(rl.Vector2(x - 4, cy), rl.Vector2(x + tex.width + 4, cy), 4, rl.Color(255, 255, 255, 165))
|
||||||
|
|||||||
Reference in New Issue
Block a user