added calibration data handling functions

This commit is contained in:
ykcheungab
2026-09-10 21:47:36 +08:00
parent 9cbba08d68
commit 96f4428230
13 changed files with 1399 additions and 175 deletions
+80
View File
@@ -1,4 +1,5 @@
import json
import shutil
from contextlib import ExitStack
from dataclasses import dataclass
from importlib.resources import as_file, files
@@ -166,6 +167,85 @@ def load_force_calibration(name: str) -> dict | None:
return _load_json(bundle.force_calibration_json)
_BUNDLE_JSON_FILES = ("sensor.json", "force_calibration.json", "manifest.json")
def export_sensor_bundle(
name: str,
dest_dir: str | Path,
*,
include_assets: bool = False,
overwrite: bool = True,
) -> Path:
"""Copy one resolved ``sensors/<name>/`` bundle into ``dest_dir/<name>/``.
By default only JSON metadata is copied (``sensor.json``, and when present
``force_calibration.json`` / ``manifest.json``). Pass ``include_assets=True``
to copy the full tree (including ``assets/``) for packaging finger meshes.
"""
bundle = resolve_sensor_bundle(name)
if bundle is None:
available = ", ".join(list_sensor_bundles()) or "none"
raise FileNotFoundError(
f"Sensor bundle not found: {name!r}. Available: {available}"
)
dest_root = Path(dest_dir).expanduser().resolve()
dest = dest_root / bundle.name
if dest.exists():
if not overwrite:
raise FileExistsError(f"Destination already exists: {dest}")
if dest.is_dir():
shutil.rmtree(dest)
else:
dest.unlink()
if include_assets:
shutil.copytree(bundle.root, dest)
return dest
dest.mkdir(parents=True, exist_ok=True)
for filename in _BUNDLE_JSON_FILES:
src = bundle.root / filename
if src.is_file():
shutil.copy2(src, dest / filename)
if not (dest / "sensor.json").is_file():
raise FileNotFoundError(f"sensor.json missing after export from {bundle.root}")
return dest
def export_sensor_bundles(
dest_dir: str | Path,
*,
names: list[str] | tuple[str, ...] | None = None,
include_assets: bool = False,
overwrite: bool = True,
) -> list[Path]:
"""Export several sensor bundles into ``dest_dir``.
When ``names`` is omitted, exports every name in ``PRODUCT_BUNDLES`` that
currently resolves via :func:`resolve_sensor_bundle`.
"""
if names is None:
selected = [name for name in PRODUCT_BUNDLES if resolve_sensor_bundle(name) is not None]
else:
selected = [str(name) for name in names]
if not selected:
raise FileNotFoundError("No sensor bundles selected for export")
exported: list[Path] = []
for name in selected:
exported.append(
export_sensor_bundle(
name,
dest_dir,
include_assets=include_assets,
overwrite=overwrite,
)
)
return exported
def load_config(config_name_or_path):
"""
Load sensor configuration from a JSON file path or config name.