summaryrefslogtreecommitdiff
path: root/devices/serial_device.py
diff options
context:
space:
mode:
authorChristian Kolset <christian.kolset@gmail.com>2026-09-19 17:38:07 -0600
committerChristian Kolset <christian.kolset@gmail.com>2026-09-19 17:38:07 -0600
commitf3327d56d48d63b21ffc03a55fa5e7db753d1707 (patch)
treee19d4c7846dd4a434f8a18f82290597731b4a12c /devices/serial_device.py
parent00671a90bb51bfe485d98a928a456d55adbe1faf (diff)
Redesign Signal/Channel naming and fix pin-remap breaking references
Arduino analog Signal ids used to be the pin name itself, so fixing a wrong pin silently changed the id and orphaned any plot trace, control, or filter pipeline pointing at it. Analog Signal ids are now positional (ai0, ai1, ...) and decoupled from the underlying pin, matching how digital I/O already worked. More broadly, formalizes a Device -> Signal -> Channel -> Plot naming model (SignalConfig for physical device channels, Channel for derived/ computed ones) and adds core/reference_registry.py to propagate any Signal or Channel id rename through plot traces, controls, and filter pipelines instead of leaving them dangling. Signal ids are now editable in the Signals tab with the same propagation. .labui profile schema bumped to v2.0 with a transparent migration for old files, including remapping old pin-name Signal ids to the new positional ones. Removed six confirmed-dead files (two legacy device classes, four superseded top-level ui/ duplicates).
Diffstat (limited to 'devices/serial_device.py')
-rw-r--r--devices/serial_device.py66
1 files changed, 33 insertions, 33 deletions
diff --git a/devices/serial_device.py b/devices/serial_device.py
index d402f09..86d5e80 100644
--- a/devices/serial_device.py
+++ b/devices/serial_device.py
@@ -31,7 +31,7 @@ from PyQt6.QtWidgets import (
QStackedWidget, QTableWidget, QTableWidgetItem, QTextEdit, QVBoxLayout, QWidget,
)
-from devices.base_device import BaseDevice, ChannelConfig, DeviceInfo, DeviceStatus
+from devices.base_device import BaseDevice, SignalConfig, DeviceInfo, DeviceStatus
from api_layers.arduino_layer import ArduinoLayer
_COLORS = ["#7fff6e", "#4cc9f0", "#f72585", "#00d4ff",
@@ -145,7 +145,7 @@ class SerialDevice(BaseDevice):
device_type=self.DEVICE_TYPE,
description=self._description(),
icon=self.ICON,
- channels=channels,
+ signals=channels,
)
super().__init__(info)
self._layer = self._make_layer()
@@ -175,21 +175,21 @@ class SerialDevice(BaseDevice):
# keep the channel's unit in sync so new plots/pickers/CSV log
# headers pick up the currently-selected unit instead of a fixed
# default. Does not relabel the axis of an already-open plot pane.
- force_ch = self.get_channel("force")
+ force_ch = self.get_signal("force")
if force_ch is not None:
force_ch.unit = self._layer.current_unit
if self._fmt not in _GENERIC_FORMATS:
- # Protocol layers already key by channel_id — pass through untouched.
+ # Protocol layers already key by signal_id — pass through untouched.
# (Write-only channels, e.g. CML "M1_VS", never appear in raw — correctly dropped.)
return raw
# Generic (ArduinoLayer) may use arbitrary names — remap by position.
mapped: Dict[str, float] = {}
raw_vals = list(raw.values())
- for i, ch in enumerate(self.info.channels):
- if ch.channel_id in raw:
- mapped[ch.channel_id] = raw[ch.channel_id]
+ for i, ch in enumerate(self.info.signals):
+ if ch.signal_id in raw:
+ mapped[ch.signal_id] = raw[ch.signal_id]
elif i < len(raw_vals):
- mapped[ch.channel_id] = raw_vals[i]
+ mapped[ch.signal_id] = raw_vals[i]
return mapped
def write_channel(self, channel_id: str, value: Any) -> bool:
@@ -217,9 +217,9 @@ class SerialDevice(BaseDevice):
cfg["port_serial"] = ArduinoLayer.port_serial_number(self._port)
if self._fmt in _GENERIC_FORMATS:
cfg.update({
- "num_channels": len(self.info.channels),
- "channel_names": [c.channel_id for c in self.info.channels],
- "units": [c.unit for c in self.info.channels],
+ "num_channels": len(self.info.signals),
+ "channel_names": [c.signal_id for c in self.info.signals],
+ "units": [c.unit for c in self.info.signals],
})
elif self._fmt == "scpi":
cfg["scpi_channels"] = self._scpi_channels
@@ -274,7 +274,7 @@ class SerialDevice(BaseDevice):
self._mb_channels = modbus_channels or self._mb_channels
self._motors = motors or self._motors
- self.info.channels = self._build_channel_configs()
+ self.info.signals = self._build_channel_configs()
self.info.description = self._description()
self._layer = self._make_layer()
@@ -287,14 +287,14 @@ class SerialDevice(BaseDevice):
label = _LABEL_FOR.get(self._fmt, self._fmt)
return f"{self._port} @ {self._baud} [{label}]"
- def _build_channel_configs(self) -> List[ChannelConfig]:
+ def _build_channel_configs(self) -> List[SignalConfig]:
fmt = self._fmt
if fmt in _GENERIC_FORMATS:
names = self._chan_names or [f"sig{i}" for i in range(self._num_channels)]
_units = self._units or [""] * len(names)
return [
- ChannelConfig(
- channel_id=names[i], name=names[i], unit=_units[i],
+ SignalConfig(
+ signal_id=names[i], name=names[i], unit=_units[i],
min_value=0.0, max_value=1023.0,
color=_COLORS[i % len(_COLORS)],
)
@@ -302,8 +302,8 @@ class SerialDevice(BaseDevice):
]
elif fmt == "scpi":
return [
- ChannelConfig(
- channel_id=sc["channel_id"],
+ SignalConfig(
+ signal_id=sc["channel_id"],
name=sc.get("name", sc["channel_id"]),
unit=sc.get("unit", ""),
min_value=-1e9, max_value=1e9,
@@ -314,8 +314,8 @@ class SerialDevice(BaseDevice):
]
elif fmt == "modbus_rtu":
return [
- ChannelConfig(
- channel_id=mc["channel_id"],
+ SignalConfig(
+ signal_id=mc["channel_id"],
name=mc.get("name", mc["channel_id"]),
unit=mc.get("unit", ""),
min_value=-1e9, max_value=1e9,
@@ -328,15 +328,15 @@ class SerialDevice(BaseDevice):
elif fmt == "mark10":
from api_layers.protocols.mark10 import UNITS as _MARK10_UNITS
return [
- ChannelConfig("force", "Force", "N", -5000.0, 5000.0, color=_COLORS[0]),
- ChannelConfig("unit_code", "Unit Code", "", 0.0, float(len(_MARK10_UNITS) - 1), color=_COLORS[1]),
+ SignalConfig("force", "Force", "N", -5000.0, 5000.0, color=_COLORS[0]),
+ SignalConfig("unit_code", "Unit Code", "", 0.0, float(len(_MARK10_UNITS) - 1), color=_COLORS[1]),
# Write-only action channels (no reading — for control buttons)
- ChannelConfig("zero", "Zero Gauge", "", 0.0, 1.0, color=_COLORS[2], writable=True),
- ChannelConfig("cycle_units", "Cycle Units", "", 0.0, 1.0, color=_COLORS[3], writable=True),
+ SignalConfig("zero", "Zero Gauge", "", 0.0, 1.0, color=_COLORS[2], writable=True),
+ SignalConfig("cycle_units", "Cycle Units", "", 0.0, 1.0, color=_COLORS[3], writable=True),
]
elif fmt == "dscusb":
return [
- ChannelConfig("extension", "Extension", "", -50.0, 50.0, color=_COLORS[0]),
+ SignalConfig("extension", "Extension", "", -50.0, 50.0, color=_COLORS[0]),
]
elif fmt == "cml":
_CMD_UNITS = {"TP": "counts", "TV": "counts/s", "TC": "%×10", "TS": "flags"}
@@ -346,8 +346,8 @@ class SerialDevice(BaseDevice):
for motor in self._motors:
mid = motor.get("motor_id", "M1")
for cmd in motor.get("read_cmds", ["TP", "TV", "TC"]):
- channels.append(ChannelConfig(
- channel_id=f"{mid}_{cmd}",
+ channels.append(SignalConfig(
+ signal_id=f"{mid}_{cmd}",
name=f"{mid} {cmd}",
unit=_CMD_UNITS.get(cmd, ""),
min_value=-1e6, max_value=1e6,
@@ -356,8 +356,8 @@ class SerialDevice(BaseDevice):
color_idx += 1
# Write-only setpoint channels (no reading — for control widgets)
for cmd in ("VS", "MA"):
- channels.append(ChannelConfig(
- channel_id=f"{mid}_{cmd}",
+ channels.append(SignalConfig(
+ signal_id=f"{mid}_{cmd}",
name=f"{mid} {cmd}",
unit=_WRITE_UNITS.get(cmd, ""),
min_value=-1e6, max_value=1e6,
@@ -367,8 +367,8 @@ class SerialDevice(BaseDevice):
color_idx += 1
# Write-only action channels — motor must be enabled (ME) before VS/MA take effect
for cmd, label in (("ME", "Enable"), ("MD", "Disable"), ("ST", "Stop")):
- channels.append(ChannelConfig(
- channel_id=f"{mid}_{cmd}",
+ channels.append(SignalConfig(
+ signal_id=f"{mid}_{cmd}",
name=f"{mid} {label}",
unit="",
min_value=0.0, max_value=1.0,
@@ -376,13 +376,13 @@ class SerialDevice(BaseDevice):
writable=True,
))
color_idx += 1
- return channels or [ChannelConfig("M1_TP", "M1 TP", "counts", color=_COLORS[0])]
+ return channels or [SignalConfig("M1_TP", "M1 TP", "counts", color=_COLORS[0])]
return []
def _make_layer(self):
fmt = self._fmt
if fmt in _GENERIC_FORMATS:
- names = [ch.channel_id for ch in self.info.channels]
+ names = [ch.signal_id for ch in self.info.signals]
return ArduinoLayer(
port=self._port, baud=self._baud,
analog_pins=names, simulate=self.simulate,
@@ -632,7 +632,7 @@ class _GenericPanel(QWidget):
self._ch_spin = QSpinBox()
self._ch_spin.setRange(1, 32)
- self._ch_spin.setValue(len(device.info.channels) if device._fmt in _GENERIC_FORMATS else 4)
+ self._ch_spin.setValue(len(device.info.signals) if device._fmt in _GENERIC_FORMATS else 4)
lay.addRow("Signals:", self._ch_spin)
# ── Diagnostics ───────────────────────────────────────────────────