Files
python3-damaris/src/damaris/gui/TemperatureMonitor.py
T

160 lines
5.6 KiB
Python

# -*- coding: iso-8859-1 -*-
"""
Temperature monitoring thread.
This module provides a thread that periodically reads temperature from a controller
and stores it in a TemperatureResult object.
"""
import threading
import time
import datetime
from damaris.data import TemperatureResult
from damaris.tools.temperature import TemperatureController
class TemperatureMonitor(threading.Thread):
"""
Thread that periodically reads temperature from a controller
and stores it in a TemperatureResult object.
This thread runs independently and can be configured with any
TemperatureController implementation (Eurotherm, Lakeshore, etc.).
Attributes:
controller: The TemperatureController instance to read from.
interval: Polling interval in seconds.
result: The TemperatureResult object storing collected data.
quit_flag: Event to signal the thread to stop.
data_pool: Optional DataPool to store results.
data_key: Key to use when storing in data_pool.
"""
def __init__(self, controller: TemperatureController,
interval: float = 1.0,
data_pool=None,
data_key: str = "temperature"):
"""
Initialize the temperature monitor.
Args:
controller: TemperatureController implementation (Eurotherm, etc.)
interval: Polling interval in seconds (default: 1.0)
data_pool: Optional DataPool to store results (default: None)
data_key: Key to use when storing in data_pool (default: "temperature")
"""
threading.Thread.__init__(self, name="TemperatureMonitor")
self.controller = controller
self.interval = float(interval)
self.data_pool = data_pool
self.data_key = data_key
self.quit_flag = threading.Event()
self.result = TemperatureResult()
self._running = False
self._lock = threading.RLock()
# Initialize the result with metadata
self.result.xlabel = "Time (s)"
self.result.ylabel = "Temperature (C)"
self.result.description = {"type": "temperature_monitoring"}
def run(self):
"""
Main thread loop: periodically reads temperature and stores it.
"""
self._running = True
start_time = time.time()
while not self.quit_flag.is_set() and self._running:
try:
# Read temperature and setpoint
timestamp = time.time() - start_time
current_temp = self.controller.get_temperature()
setpoint = self.controller.get_setpoint()
# Store in result
with self._lock:
if not hasattr(self.result, 'x'):
self.result.x = []
self.result.y = []
self.result.setpoint = []
self.result.x.append(timestamp)
self.result.y.append(current_temp)
self.result.setpoint.append(setpoint)
# Update job metadata on first sample
if len(self.result.x) == 1:
self.result.job_date = datetime.datetime.now()
self.result.job_id = id(self)
# Optional: store in data_pool
if self.data_pool is not None:
with self._lock:
self.data_pool[self.data_key] = self.result
except Exception as e:
print(f"TemperatureMonitor error: {e}")
import traceback
traceback.print_exc()
# Sleep in small increments for responsive shutdown
sleep_remaining = self.interval
while sleep_remaining > 0 and not self.quit_flag.is_set() and self._running:
sleep_time = min(0.1, sleep_remaining)
self.quit_flag.wait(sleep_time)
sleep_remaining -= sleep_time
def stop(self):
"""
Graceful shutdown: signal the thread to stop and wait for it.
"""
self._running = False
self.quit_flag.set()
# Wait for the thread to finish its current iteration
self.join(timeout=self.interval + 0.5)
def reset(self):
"""
Clear the collected data and start fresh.
"""
with self._lock:
self.result = TemperatureResult()
self.result.xlabel = "Time (s)"
self.result.ylabel = "Temperature (C)"
self.result.description = {"type": "temperature_monitoring"}
def get_current_temperature(self) -> float:
"""
Get the latest temperature reading.
Returns:
float: Latest temperature in Celsius, or 0.0 if no data.
"""
with self._lock:
if hasattr(self.result, 'y') and len(self.result.y) > 0:
return self.result.y[-1]
return 0.0
def get_current_setpoint(self) -> float:
"""
Get the latest setpoint reading.
Returns:
float: Latest setpoint in Celsius, or 0.0 if no data.
"""
with self._lock:
if hasattr(self.result, 'setpoint') and len(self.result.setpoint) > 0:
return self.result.setpoint[-1]
return 0.0
def is_running(self) -> bool:
"""
Check if the monitor is currently running.
Returns:
bool: True if the monitor thread is active.
"""
return self._running and not self.quit_flag.is_set()