""" ui/add_device_dialog.py — Dialog to add a new device at runtime. """ from PyQt6.QtWidgets import ( QDialog, QVBoxLayout, QFormLayout, QHBoxLayout, QComboBox, QLineEdit, QSpinBox, QCheckBox, QPushButton, QLabel, QMessageBox, ) from devices.device_registry import DeviceRegistry from devices.analog_input import AnalogInputDevice from devices.digital_io import DigitalIODevice from devices.serial_device import SerialDevice # Map display name -> (class, extra_kwargs_defaults) _DEVICE_TYPES = { "Analog Input — NI-DAQmx": (AnalogInputDevice, {"backend": "nidaqmx"}), "Analog Input — Arduino": (AnalogInputDevice, {"backend": "arduino"}), "Digital I/O — NI-DAQmx": (DigitalIODevice, {"backend": "nidaqmx"}), "Digital I/O — Arduino": (DigitalIODevice, {"backend": "arduino"}), "Serial / UART": (SerialDevice, {}), } class AddDeviceDialog(QDialog): def __init__(self, registry: DeviceRegistry, parent=None): super().__init__(parent) self.registry = registry self.created_device = None self.setWindowTitle("Add Device") self.setMinimumWidth(360) self._build() def _build(self): layout = QVBoxLayout(self) form = QFormLayout() self._type_cb = QComboBox() self._type_cb.addItems(list(_DEVICE_TYPES.keys())) form.addRow("Device Type:", self._type_cb) self._id_edit = QLineEdit() self._id_edit.setPlaceholderText("e.g. ai_1 / ser_0") form.addRow("Device ID:", self._id_edit) self._ch_spin = QSpinBox() self._ch_spin.setRange(1, 16) self._ch_spin.setValue(4) form.addRow("# Channels:", self._ch_spin) self._sim_chk = QCheckBox("Simulation mode (no hardware required)") self._sim_chk.setChecked(True) form.addRow(self._sim_chk) layout.addLayout(form) btns = QHBoxLayout() btns.addStretch() cancel = QPushButton("Cancel") cancel.clicked.connect(self.reject) add = QPushButton("Add Device") add.setDefault(True) add.setObjectName("applyButton") add.clicked.connect(self._on_add) btns.addWidget(cancel) btns.addWidget(add) layout.addLayout(btns) def _on_add(self): label = self._type_cb.currentText() cls, kw = _DEVICE_TYPES[label] dev_id = self._id_edit.text().strip() if not dev_id: base = kw.get("backend", "dev") existing = {d.info.device_id for d in self.registry.all_instances()} for i in range(100): candidate = f"{base}_{i}" if candidate not in existing: dev_id = candidate break if self.registry.get_instance(dev_id): QMessageBox.warning(self, "Duplicate ID", f"A device with ID '{dev_id}' already exists.") return try: dev = cls( device_id=dev_id, num_channels=self._ch_spin.value(), simulate=self._sim_chk.isChecked(), **kw, ) self.created_device = dev self.accept() except Exception as e: QMessageBox.critical(self, "Error creating device", str(e))