# -*- 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()