127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
import json
|
|
from pathlib import Path
|
|
|
|
|
|
def _candidate_config_dirs() -> list[Path]:
|
|
"""
|
|
Candidate config directories for editable installs and packaged wheels.
|
|
"""
|
|
here = Path(__file__).resolve().parent
|
|
candidates = [
|
|
here / "configs", # packaged wheel layout: orisys/configs/*.json
|
|
here.parent.parent / "config", # editable install from repo: repo_root/config/*.json
|
|
here.parent / "config", # fallback
|
|
Path.cwd() / "config", # script launched from repo root
|
|
]
|
|
|
|
unique: list[Path] = []
|
|
for path in candidates:
|
|
if path not in unique:
|
|
unique.append(path)
|
|
return unique
|
|
|
|
|
|
def _get_config_dir() -> Path:
|
|
for candidate in _candidate_config_dirs():
|
|
if candidate.exists() and any(candidate.glob("*.json")):
|
|
return candidate
|
|
# Default to the first candidate so error messages are stable.
|
|
return _candidate_config_dirs()[0]
|
|
|
|
|
|
CONFIG_DIR = _get_config_dir()
|
|
|
|
|
|
def _config_files() -> list[Path]:
|
|
if not CONFIG_DIR.exists():
|
|
return []
|
|
return sorted(CONFIG_DIR.glob("*.json"))
|
|
|
|
|
|
def _load_json(path: Path) -> dict:
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
|
|
|
|
def load_config(config_name_or_path):
|
|
"""
|
|
Load sensor configuration from a JSON file path or config name.
|
|
|
|
Config lookup order:
|
|
1. Exact file path (absolute or relative)
|
|
2. File stem under the config directory (e.g. ``ddjx01`` -> ``config/ddjx01.json``)
|
|
3. JSON field ``"name"`` matching the requested name (keeps ``prototype`` compatible)
|
|
"""
|
|
config_path = Path(config_name_or_path)
|
|
|
|
if config_path.exists() and config_path.is_file():
|
|
if config_path.suffix.lower() != ".json":
|
|
raise ValueError(f"Config file must be a JSON file: {config_path}")
|
|
return _load_json(config_path)
|
|
|
|
if config_path.is_absolute():
|
|
raise FileNotFoundError(f"Config file not found at absolute path: {config_path}")
|
|
|
|
direct = CONFIG_DIR / f"{config_name_or_path}.json"
|
|
if direct.exists():
|
|
return _load_json(direct)
|
|
|
|
# Backward-compatible alias resolution: support old names via JSON "name" field.
|
|
for candidate in _config_files():
|
|
try:
|
|
data = _load_json(candidate)
|
|
except Exception:
|
|
continue
|
|
if data.get("name") == config_name_or_path:
|
|
return data
|
|
|
|
available = [f.stem for f in _config_files()]
|
|
available_str = ", ".join(available) if available else "none"
|
|
raise FileNotFoundError(
|
|
f"Input config file not found with name or path: {config_name_or_path}\n"
|
|
f" Tried as file path: {config_path} (not found)\n"
|
|
f" Tried as config file: {direct} (not found)\n"
|
|
f" Available config files: {available_str}"
|
|
)
|
|
|
|
|
|
def list_config():
|
|
"""
|
|
List all available JSON configuration files by file stem.
|
|
"""
|
|
return [f.stem for f in _config_files()]
|
|
|
|
|
|
def export_config(config_name=None, path="./config/exported_config.json"):
|
|
"""
|
|
Export a configuration to a JSON file with readable formatting.
|
|
"""
|
|
config = load_config(config_name)
|
|
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
with open(path, "w", encoding="utf-8") as f:
|
|
formatted = _format_json_value(config)
|
|
f.write(formatted)
|
|
f.write("\n")
|
|
|
|
print(f"Config name: {config_name} exported to {path}")
|
|
return config
|
|
|
|
|
|
def _format_json_value(value, indent_level=0, indent_str=" "):
|
|
if isinstance(value, dict):
|
|
if not value:
|
|
return "{}"
|
|
items = []
|
|
for k, v in value.items():
|
|
key_str = json.dumps(k, ensure_ascii=False)
|
|
value_str = _format_json_value(v, indent_level + 1, indent_str)
|
|
items.append(f"{indent_str * (indent_level + 1)}{key_str}: {value_str}")
|
|
return "{\n" + ",\n".join(items) + "\n" + indent_str * indent_level + "}"
|
|
if isinstance(value, list):
|
|
if not value:
|
|
return "[]"
|
|
items = [json.dumps(item, ensure_ascii=False) for item in value]
|
|
return "[" + ", ".join(items) + "]"
|
|
return json.dumps(value, ensure_ascii=False)
|