diff options
| author | Christian Kolset <christian.kolset@gmail.com> | 2026-04-13 14:36:55 -0600 |
|---|---|---|
| committer | Christian Kolset <christian.kolset@gmail.com> | 2026-04-13 14:36:55 -0600 |
| commit | 8d6acf3a8ea4b37f86b321dbf430be5be01b1267 (patch) | |
| tree | 923251b7ee88c7b5c89500883154bad32a2ecca5 /daq_system/devices/device_registry.py | |
init
Diffstat (limited to 'daq_system/devices/device_registry.py')
| -rw-r--r-- | daq_system/devices/device_registry.py | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/daq_system/devices/device_registry.py b/daq_system/devices/device_registry.py new file mode 100644 index 0000000..e407bc3 --- /dev/null +++ b/daq_system/devices/device_registry.py @@ -0,0 +1,87 @@ +""" +devices/device_registry.py + +Discovers, instantiates, and manages all device modules. +Add new devices by dropping a .py file into the devices/ directory. +""" + +import importlib +import inspect +import pkgutil +from pathlib import Path +from typing import Dict, List, Optional, Type + +from devices.base_device import BaseDevice, DeviceInfo + + +class DeviceRegistry: + """Central registry for all DAQ device modules.""" + + def __init__(self): + self._device_classes: Dict[str, Type[BaseDevice]] = {} + self._instances: Dict[str, BaseDevice] = {} + self._auto_discover() + + # ------------------------------------------------------------------ # + # Discovery # + # ------------------------------------------------------------------ # + + def _auto_discover(self): + """Scan the devices/ package for BaseDevice subclasses.""" + devices_path = Path(__file__).parent + package = "devices" + + for _, module_name, _ in pkgutil.iter_modules([str(devices_path)]): + if module_name.startswith("_") or module_name in ("base_device", "device_registry"): + continue + try: + module = importlib.import_module(f"{package}.{module_name}") + for name, obj in inspect.getmembers(module, inspect.isclass): + if issubclass(obj, BaseDevice) and obj is not BaseDevice: + self._device_classes[name] = obj + except Exception as e: + print(f"[DeviceRegistry] Failed to load {module_name}: {e}") + + def register_class(self, cls: Type[BaseDevice]) -> None: + """Manually register a device class (for testing / runtime plugins).""" + self._device_classes[cls.__name__] = cls + + # ------------------------------------------------------------------ # + # Instance management # + # ------------------------------------------------------------------ # + + def create_device(self, class_name: str, device_id: str, **kwargs) -> Optional[BaseDevice]: + """Instantiate a device by class name with a unique device_id.""" + cls = self._device_classes.get(class_name) + if cls is None: + raise ValueError(f"Unknown device class: {class_name}") + instance = cls(device_id=device_id, **kwargs) + self._instances[device_id] = instance + return instance + + def add_instance(self, device: BaseDevice) -> None: + """Register a pre-built device instance.""" + self._instances[device.info.device_id] = device + + def remove_instance(self, device_id: str) -> None: + dev = self._instances.pop(device_id, None) + if dev: + try: + dev.disconnect() + except Exception: + pass + + def get_instance(self, device_id: str) -> Optional[BaseDevice]: + return self._instances.get(device_id) + + def all_instances(self) -> List[BaseDevice]: + return list(self._instances.values()) + + def available_classes(self) -> List[str]: + return list(self._device_classes.keys()) + + def get_class(self, class_name: str) -> Optional[Type[BaseDevice]]: + return self._device_classes.get(class_name) + + def __len__(self): + return len(self._instances) |
