diff options
Diffstat (limited to 'daq_system/devices/temperature.py')
| -rw-r--r-- | daq_system/devices/temperature.py | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/daq_system/devices/temperature.py b/daq_system/devices/temperature.py new file mode 100644 index 0000000..5427cf6 --- /dev/null +++ b/daq_system/devices/temperature.py @@ -0,0 +1,115 @@ +""" +devices/temperature.py + +Temperature sensor module — thermocouple / RTD / thermistor inputs. +""" + +import math +import random +import time +from typing import Any, Dict + +from PyQt6.QtWidgets import ( + QWidget, QVBoxLayout, QFormLayout, QGroupBox, + QComboBox, QDoubleSpinBox, QLabel, QCheckBox +) +from devices.base_device import BaseDevice, ChannelConfig, DeviceInfo, DeviceStatus + +TEMP_COLORS = ["#ff6b35", "#ffcc00", "#c77dff", "#ff4d6d"] + + +class TemperatureDevice(BaseDevice): + DEVICE_TYPE = "temperature" + ICON = "🌡" + + def __init__(self, device_id: str = "temp_0", num_channels: int = 4, + simulate: bool = True, sensor_type: str = "thermocouple"): + channels = [ + ChannelConfig( + channel_id=f"tc{i}", name=f"TC {i}", + unit="°C", min_value=-200.0, max_value=1200.0, + alarm_low=0.0, alarm_high=100.0, + color=TEMP_COLORS[i % len(TEMP_COLORS)] + ) + for i in range(num_channels) + ] + info = DeviceInfo( + device_id=device_id, name="Temperature", + device_type=self.DEVICE_TYPE, + description=f"{sensor_type.title()} temperature input", + icon=self.ICON, channels=channels + ) + super().__init__(info) + self.simulate = simulate + self.sensor_type = sensor_type + self._start = time.time() + # Simulate slow thermal drift + self._targets = [20.0 + i * 5 for i in range(num_channels)] + self._currents = [20.0 + i * 5 for i in range(num_channels)] + + def connect(self) -> bool: + self._start = time.time() + self.status = DeviceStatus.SIMULATED if self.simulate else DeviceStatus.ERROR + return self.simulate + + def disconnect(self) -> None: + self.status = DeviceStatus.DISCONNECTED + + def read_channels(self) -> Dict[str, float]: + result = {} + for i, ch in enumerate(self.info.channels): + if not ch.enabled: + continue + # Slow drift toward target with noise + diff = self._targets[i] - self._currents[i] + self._currents[i] += diff * 0.05 + random.gauss(0, 0.02) + # Occasionally shift target + if random.random() < 0.01: + self._targets[i] += random.gauss(0, 2.0) + self._targets[i] = max(10.0, min(200.0, self._targets[i])) + result[ch.channel_id] = round(self._currents[i], 2) + return result + + def write_channel(self, channel_id: str, value: Any) -> bool: + return False # Read-only + + def get_config_widget(self) -> QWidget: + w = QWidget() + layout = QVBoxLayout(w) + grp = QGroupBox("Sensor Configuration") + form = QFormLayout(grp) + + sensor_cb = QComboBox() + sensor_cb.addItems(["thermocouple", "rtd", "thermistor", "ic_sensor"]) + sensor_cb.setCurrentText(self.sensor_type) + form.addRow("Sensor Type:", sensor_cb) + + tc_type = QComboBox() + tc_type.addItems(["K", "J", "T", "E", "N", "R", "S", "B"]) + form.addRow("TC Type:", tc_type) + + unit_cb = QComboBox() + unit_cb.addItems(["°C", "°F", "K"]) + form.addRow("Units:", unit_cb) + + layout.addWidget(grp) + + # Alarm config per channel + alarm_grp = QGroupBox("Alarm Setpoints") + alarm_layout = QVBoxLayout(alarm_grp) + for ch in self.info.channels: + row_layout = QFormLayout() + lo = QDoubleSpinBox() + lo.setRange(-200, 1200) + lo.setValue(ch.alarm_low or 0.0) + lo.setSuffix(" °C") + hi = QDoubleSpinBox() + hi.setRange(-200, 1200) + hi.setValue(ch.alarm_high or 100.0) + hi.setSuffix(" °C") + row_layout.addRow(f"{ch.name} Low:", lo) + row_layout.addRow(f"{ch.name} High:", hi) + alarm_layout.addLayout(row_layout) + layout.addWidget(alarm_grp) + layout.addStretch() + return w |
