diff --git a/config/compound_eye.png b/config/compound_eye.png index b4d46e7..383e5b6 100644 Binary files a/config/compound_eye.png and b/config/compound_eye.png differ diff --git a/internal/run_operator.py b/internal/run_operator.py index a7a9fa7..f94a668 100644 --- a/internal/run_operator.py +++ b/internal/run_operator.py @@ -22,11 +22,21 @@ for path in (OPERATOR_SRC, PUBLIC_SRC): if str(path) not in sys.path: sys.path.insert(0, str(path)) -# Prefer sibling orisys-sdk checkout for Sensor-owned preview. +# Prefer sibling checkouts for editable Sensor-owned preview. +ORISYS_COMMON_SRC = REPO.parent / "orisys-common" / "src" +if ORISYS_COMMON_SRC.is_dir() and str(ORISYS_COMMON_SRC) not in sys.path: + sys.path.insert(0, str(ORISYS_COMMON_SRC)) + ORISYS_SRC = REPO.parent / "orisys-sdk" / "src" if ORISYS_SRC.is_dir() and str(ORISYS_SRC) not in sys.path: sys.path.insert(0, str(ORISYS_SRC)) +from orisys_common.runtime import configure_app_root # noqa: E402 +from sensor_bench.paths import prefer_local_sensor_bundles # noqa: E402 + +configure_app_root(REPO) +prefer_local_sensor_bundles(REPO) + from sensor_bench_operator.__main__ import main # noqa: E402 diff --git a/run.py b/run.py index 0450240..f18dbdd 100644 --- a/run.py +++ b/run.py @@ -22,11 +22,21 @@ SRC = ROOT / "src" if str(SRC) not in sys.path: sys.path.insert(0, str(SRC)) -# Prefer sibling orisys-sdk checkout for Sensor-owned preview. +# Prefer sibling checkouts for editable Sensor-owned preview. +ORISYS_COMMON_SRC = ROOT.parent / "orisys-common" / "src" +if ORISYS_COMMON_SRC.is_dir() and str(ORISYS_COMMON_SRC) not in sys.path: + sys.path.insert(0, str(ORISYS_COMMON_SRC)) + ORISYS_SRC = ROOT.parent / "orisys-sdk" / "src" if ORISYS_SRC.is_dir() and str(ORISYS_SRC) not in sys.path: sys.path.insert(0, str(ORISYS_SRC)) +from orisys_common.runtime import configure_app_root # noqa: E402 +from sensor_bench.paths import prefer_local_sensor_bundles # noqa: E402 + +configure_app_root(ROOT) +prefer_local_sensor_bundles(ROOT) + from sensor_bench.main import main # noqa: E402 diff --git a/src/sensor_bench/main.py b/src/sensor_bench/main.py index 42dfa36..d3e7c79 100644 --- a/src/sensor_bench/main.py +++ b/src/sensor_bench/main.py @@ -10,8 +10,12 @@ os.environ.setdefault("OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS", "0") from PyQt5.QtWidgets import QApplication +from .paths import app_root, prefer_local_sensor_bundles + def main(argv: list[str] | None = None) -> int: + prefer_local_sensor_bundles(app_root()) + parser_args = list(sys.argv[1:] if argv is None else argv[1:]) # Accept unknown Qt args (e.g. -platform offscreen) without advertising # internal-only flags on the public CLI. diff --git a/src/sensor_bench/paths.py b/src/sensor_bench/paths.py index 3b7bf84..6015db6 100644 --- a/src/sensor_bench/paths.py +++ b/src/sensor_bench/paths.py @@ -129,6 +129,57 @@ def list_local_sensor_bundle_names() -> list[str]: return found +_PREFERRED_SENSOR_ROOT: Path | None = None + + +def prefer_local_sensor_bundles(root: Path | None = None) -> Path | None: + """Make ``root/sensors`` win over packaged/SDK bundles for Orisys lookups. + + Wraps ``orisys.configs._candidate_sensor_roots`` so consumer + ``sensors//sensor.json`` and ``force_calibration.json`` resolve + before orisys-sdk or wheel copies. Idempotent. + """ + global _PREFERRED_SENSOR_ROOT + + resolved_root = Path(root).resolve() if root is not None else app_root() + try: + from orisys_common.runtime import configure_app_root + + configure_app_root(resolved_root) + except Exception: # noqa: BLE001 + pass + + sensors = (resolved_root / "sensors").resolve() + if not sensors.is_dir(): + return None + + if _PREFERRED_SENSOR_ROOT is not None and _PREFERRED_SENSOR_ROOT == sensors: + return sensors + + try: + import orisys.configs as configs + except Exception: # noqa: BLE001 + return None + + original = configs._candidate_sensor_roots + if getattr(original, "_sensor_bench_local_pref", None) == str(sensors): + _PREFERRED_SENSOR_ROOT = sensors + return sensors + + def _candidate_sensor_roots_prefer_local() -> list[Path]: + roots = [sensors] + for candidate in original(): + resolved = Path(candidate).resolve() + if resolved != sensors and resolved not in roots: + roots.append(resolved) + return roots + + _candidate_sensor_roots_prefer_local._sensor_bench_local_pref = str(sensors) # type: ignore[attr-defined] + configs._candidate_sensor_roots = _candidate_sensor_roots_prefer_local + _PREFERRED_SENSOR_ROOT = sensors + return sensors + + def _orisys_bundle_has_force_calibration(name: str) -> bool: try: import orisys @@ -260,6 +311,9 @@ def list_orisys_configs() -> list[str]: continue if key.lower() in PRODUCT_BUNDLES: continue + mapped = LEGACY_STEM_TO_BUNDLE.get(key.lower()) + if mapped and mapped.lower() in seen_stems: + continue entries.append(key) seen_stems.add(key.lower()) except Exception: # noqa: BLE001 diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 54c58e6..500ffec 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -262,6 +262,33 @@ def test_normalize_sensor_config_ref_prefers_bundle() -> None: assert normalize_sensor_config_ref("box") == str(local_json.resolve()) +def test_prefer_local_sensor_bundles_for_force_calibration() -> None: + import orisys + + from sensor_bench.paths import app_root, prefer_local_sensor_bundles + + root = app_root() + local_box = (root / "sensors" / "box").resolve() + local_cal = local_box / "force_calibration.json" + if not local_box.is_dir() or not local_cal.is_file(): + return + + preferred = prefer_local_sensor_bundles(root) + assert preferred == (root / "sensors").resolve() + + # Second call must stay idempotent (no stacked wrappers). + assert prefer_local_sensor_bundles(root) == preferred + roots = orisys.configs._candidate_sensor_roots() + assert Path(roots[0]).resolve() == preferred + assert sum(1 for r in roots if Path(r).resolve() == preferred) == 1 + + bundle = orisys.configs.resolve_sensor_bundle("box") + assert bundle is not None + assert bundle.root == local_box + assert bundle.force_calibration_json is not None + assert bundle.force_calibration_json.resolve() == local_cal.resolve() + + def test_roi_geometry_helpers() -> None: from sensor_bench.roi_geometry import build_roi_warp, order_quad_points, warp_roi_to_rectangle import numpy as np @@ -375,6 +402,7 @@ if __name__ == "__main__": test_default_config_prefers_box_bundle() test_legacy_config_force_calibration_fallback() test_normalize_sensor_config_ref_prefers_bundle() + test_prefer_local_sensor_bundles_for_force_calibration() test_roi_geometry_helpers() test_exhibition_window_offscreen() print("ok")