summaryrefslogtreecommitdiff
path: root/devices
diff options
context:
space:
mode:
Diffstat (limited to 'devices')
-rw-r--r--devices/__init__.py3
-rw-r--r--devices/__pycache__/__init__.cpython-312.pycbin396 -> 290 bytes
-rw-r--r--devices/__pycache__/analog_input.cpython-312.pycbin23750 -> 24161 bytes
-rw-r--r--devices/__pycache__/base_device.cpython-312.pycbin5615 -> 5604 bytes
-rw-r--r--devices/__pycache__/device_registry.cpython-312.pycbin4582 -> 4579 bytes
-rw-r--r--devices/__pycache__/digital_io.cpython-312.pycbin18353 -> 21995 bytes
-rw-r--r--devices/__pycache__/serial_device.cpython-312.pycbin14680 -> 14645 bytes
-rw-r--r--devices/analog_input.py36
-rw-r--r--devices/digital_io.py73
9 files changed, 92 insertions, 20 deletions
diff --git a/devices/__init__.py b/devices/__init__.py
index f79b333..7cee4ac 100644
--- a/devices/__init__.py
+++ b/devices/__init__.py
@@ -1,5 +1,4 @@
-# devices/__init__.py
-from devices.base_device import BaseDevice, ChannelConfig, DeviceInfo, DeviceStatus
+# devices/__init__.py from devices.base_device import BaseDevice, ChannelConfig, DeviceInfo, DeviceStatus
from devices.device_registry import DeviceRegistry
__all__ = ["BaseDevice", "ChannelConfig", "DeviceInfo", "DeviceStatus", "DeviceRegistry"]
diff --git a/devices/__pycache__/__init__.cpython-312.pyc b/devices/__pycache__/__init__.cpython-312.pyc
index bd9bb71..7c1bbbd 100644
--- a/devices/__pycache__/__init__.cpython-312.pyc
+++ b/devices/__pycache__/__init__.cpython-312.pyc
Binary files differ
diff --git a/devices/__pycache__/analog_input.cpython-312.pyc b/devices/__pycache__/analog_input.cpython-312.pyc
index 2ff8a31..fefede6 100644
--- a/devices/__pycache__/analog_input.cpython-312.pyc
+++ b/devices/__pycache__/analog_input.cpython-312.pyc
Binary files differ
diff --git a/devices/__pycache__/base_device.cpython-312.pyc b/devices/__pycache__/base_device.cpython-312.pyc
index f665ff1..bfab8b5 100644
--- a/devices/__pycache__/base_device.cpython-312.pyc
+++ b/devices/__pycache__/base_device.cpython-312.pyc
Binary files differ
diff --git a/devices/__pycache__/device_registry.cpython-312.pyc b/devices/__pycache__/device_registry.cpython-312.pyc
index 2694da3..d8b72e0 100644
--- a/devices/__pycache__/device_registry.cpython-312.pyc
+++ b/devices/__pycache__/device_registry.cpython-312.pyc
Binary files differ
diff --git a/devices/__pycache__/digital_io.cpython-312.pyc b/devices/__pycache__/digital_io.cpython-312.pyc
index 8c7375b..7fa10a5 100644
--- a/devices/__pycache__/digital_io.cpython-312.pyc
+++ b/devices/__pycache__/digital_io.cpython-312.pyc
Binary files differ
diff --git a/devices/__pycache__/serial_device.cpython-312.pyc b/devices/__pycache__/serial_device.cpython-312.pyc
index 7472878..5cacb6b 100644
--- a/devices/__pycache__/serial_device.cpython-312.pyc
+++ b/devices/__pycache__/serial_device.cpython-312.pyc
Binary files differ
diff --git a/devices/analog_input.py b/devices/analog_input.py
index 508804f..e1b8b09 100644
--- a/devices/analog_input.py
+++ b/devices/analog_input.py
@@ -90,11 +90,13 @@ class AnalogInputDevice(BaseDevice):
ni_device, ni_min_v, ni_max_v,
ard_port, ard_baud, channel_ids):
if backend == "arduino":
- return ArduinoLayer(
+ # Use shared layer so two devices on the same port don't conflict
+ from api_layers.port_registry import port_registry
+ return port_registry.get_layer(
port=ard_port,
baud=ard_baud,
- analog_pins=channel_ids,
simulate=simulate,
+ extra_pins=channel_ids,
)
else:
return NidaqmxLayer(
@@ -110,13 +112,17 @@ class AnalogInputDevice(BaseDevice):
def connect(self) -> bool:
self._last_error = ""
- # ArduinoLayer uses connect(); NidaqmxLayer uses start()
+ # ArduinoLayer: use connect_if_needed so shared layers aren't
+ # opened twice when multiple devices share the same port.
+ # NidaqmxLayer: uses start()
if hasattr(self._layer, "connect"):
- ok = self._layer.connect()
+ if self._layer.is_connected:
+ ok = True # already open — shared with another device
+ else:
+ ok = self._layer.connect()
else:
ok = self._layer.start()
- # Surface the underlying error message if available
if not ok and hasattr(self._layer, "last_error"):
self._last_error = self._layer.last_error
@@ -128,13 +134,19 @@ class AnalogInputDevice(BaseDevice):
return ok
def disconnect(self) -> None:
- try:
- if hasattr(self._layer, "disconnect"):
- self._layer.disconnect()
- elif hasattr(self._layer, "stop"):
- self._layer.stop()
- except Exception:
- pass
+ # For shared Arduino layers, release our ref count via port_registry.
+ # The layer stays open until the last device using it disconnects.
+ if self.backend == "arduino" and not self.simulate:
+ from api_layers.port_registry import port_registry
+ port_registry.release(self._ard_port, self._ard_baud)
+ else:
+ try:
+ if hasattr(self._layer, "stop"):
+ self._layer.stop()
+ elif hasattr(self._layer, "disconnect"):
+ self._layer.disconnect()
+ except Exception:
+ pass
self.status = DeviceStatus.DISCONNECTED
def read_channels(self) -> Dict[str, float]:
diff --git a/devices/digital_io.py b/devices/digital_io.py
index 53014b5..3d24192 100644
--- a/devices/digital_io.py
+++ b/devices/digital_io.py
@@ -126,12 +126,18 @@ class DigitalIODevice(BaseDevice):
return False
def _ard_connect(self) -> bool:
- from api_layers.arduino_layer import ArduinoLayer
- self._ard_layer = ArduinoLayer(
- port=self._ard_port, baud=self._ard_baud,
- digital_pins=[c.channel_id for c in self.info.channels if c.channel_id.startswith("di")],
+ from api_layers.port_registry import port_registry
+ # Get shared layer — won't open a second connection if AnalogInputDevice
+ # is already connected on the same port
+ self._ard_layer = port_registry.get_layer(
+ port=self._ard_port,
+ baud=self._ard_baud,
simulate=False,
)
+ if self._ard_layer.is_connected:
+ # Port already open — just register ourselves and return OK
+ self.status = DeviceStatus.CONNECTED
+ return True
ok = self._ard_layer.connect()
self.status = DeviceStatus.CONNECTED if ok else DeviceStatus.ERROR
return ok
@@ -143,8 +149,10 @@ class DigitalIODevice(BaseDevice):
if self._ni_out_task:
try: self._ni_out_task.stop(); self._ni_out_task.close()
except Exception: pass
- if self._ard_layer:
- self._ard_layer.disconnect()
+ if self._ard_layer and self.backend == "arduino" and not self.simulate:
+ from api_layers.port_registry import port_registry
+ port_registry.release(self._ard_port, self._ard_baud)
+ self._ard_layer = None
self.status = DeviceStatus.DISCONNECTED
def read_channels(self) -> Dict[str, float]:
@@ -226,6 +234,24 @@ class DigitalIODevice(BaseDevice):
return self._ard_layer.set_parameter(name, value)
return False
+ def switch_backend(self, backend: str, simulate: bool,
+ ni_device: str, ard_port: str, ard_baud: int) -> None:
+ was_running = self.status in (DeviceStatus.CONNECTED, DeviceStatus.SIMULATED)
+ if was_running:
+ self.disconnect()
+
+ self.backend = backend
+ self.simulate = simulate
+ self._ni_device = ni_device
+ self._ard_port = ard_port
+ self._ard_baud = ard_baud
+ self._ard_layer = None
+ self._ni_in_task = None
+ self._ni_out_task = None
+
+ if was_running:
+ self.connect()
+
def get_config_widget(self) -> QWidget:
return DigitalIOConfigWidget(self)
@@ -251,7 +277,27 @@ class DigitalIOConfigWidget(QWidget):
self.sim_chk = QCheckBox("Simulate")
self.sim_chk.setChecked(self.device.simulate)
be_form.addRow(self.sim_chk)
+
+ self.ni_dev_edit = QLineEdit(self.device._ni_device)
+ be_form.addRow("NI Device:", self.ni_dev_edit)
+
+ self.ard_port_edit = QLineEdit(self.device._ard_port)
+ self.ard_port_edit.setPlaceholderText("e.g. /dev/ttyUSB0 or COM3")
+ be_form.addRow("Arduino Port:", self.ard_port_edit)
+
+ self.ard_baud_cb = QComboBox()
+ self.ard_baud_cb.addItems(["9600", "19200", "57600", "115200", "230400"])
+ self.ard_baud_cb.setCurrentText(str(self.device._ard_baud))
+ be_form.addRow("Baud Rate:", self.ard_baud_cb)
+
+ apply_btn = QPushButton("Apply & Reconnect")
+ apply_btn.setObjectName("applyButton")
+ apply_btn.clicked.connect(self._apply)
+ be_form.addRow(apply_btn)
+
+ self.be_cb.currentTextChanged.connect(self._update_visibility)
root.addWidget(be_grp)
+ self._update_visibility(self.device.backend)
out_grp = QGroupBox("Digital Outputs")
out_lay = QVBoxLayout(out_grp)
@@ -277,3 +323,18 @@ class DigitalIOConfigWidget(QWidget):
root.addWidget(out_grp)
root.addStretch()
+
+ def _update_visibility(self, backend: str = ""):
+ backend = backend or self.be_cb.currentText()
+ self.ni_dev_edit.setVisible(backend == "nidaqmx")
+ self.ard_port_edit.setVisible(backend == "arduino")
+ self.ard_baud_cb.setVisible(backend == "arduino")
+
+ def _apply(self):
+ self.device.switch_backend(
+ backend=self.be_cb.currentText(),
+ simulate=self.sim_chk.isChecked(),
+ ni_device=self.ni_dev_edit.text().strip() or "Dev1",
+ ard_port=self.ard_port_edit.text().strip(),
+ ard_baud=int(self.ard_baud_cb.currentText()),
+ )