# -*- coding: iso-8859-1 -*- from .Resultable import Resultable from .Drawable import Drawable import numpy import threading ############################################################################# # # # Name: Class Temp_Result # # # # Purpose: Specialised class of Resultable and Drawable # # Contains recorded temperature data # # # ############################################################################# class TemperatureResult(Resultable, Drawable): """ Specialised class of Resultable and Drawable Contains recorded temperature data. Attributes: x: List of timestamps in seconds. y: List of temperature readings in Celsius. setpoint: Optional list of setpoint temperatures in Celsius. xlabel: Label for x-axis (default: "Time (s)"). ylabel: Label for y-axis (default: "Temperature (C)"). """ def __init__(self, x = None, y = None, setpoint = None, desc = None, job_id = None, job_date = None): Resultable.__init__(self) Drawable.__init__(self) # Set default labels self.xlabel = "Time (s)" self.ylabel = "Temperature (C)" # Initialize data lists self.x = [] if x is None else x self.y = [] if y is None else y self.setpoint = [] if setpoint is None else setpoint # Set metadata if provided if desc is not None: self.description = desc if job_id is not None: self.job_id = job_id if job_date is not None: self.job_date = job_date # Listener management for GUI updates self.__listeners = [] self.__listener_lock = threading.Lock() def get_number_of_channels(self): """Returns 1 for temperature data.""" return 1 def register_listener(self, listener): """Register a listener to be notified when data changes.""" with self.__listener_lock: if listener not in self.__listeners: self.__listeners.append(listener) def unregister_listener(self, listener): """Unregister a listener.""" with self.__listener_lock: if listener in self.__listeners: self.__listeners.remove(listener) def get_xdata(self): """Returns the x data (timestamps) as numpy array.""" if isinstance(self.x, numpy.ndarray): return self.x return numpy.array(self.x, dtype=float) if self.x else numpy.array([], dtype=float) def get_ydata(self, channel=0): """Returns the y data (temperature readings) as numpy array.""" if channel == 0: if isinstance(self.y, numpy.ndarray): return self.y return numpy.array(self.y, dtype=float) if self.y else numpy.array([], dtype=float) return numpy.array([], dtype=float) def __len__(self): """Returns the number of data points.""" return len(self.y) if self.y else 0 def uses_statistics(self): """Returns False as temperature data doesn't use statistics.""" return False def ready_for_drawing_error(self): """Returns False as temperature data doesn't have error bars.""" return False def get_yerr(self, channel=0): """Returns empty error data.""" return numpy.array([], dtype=float) def get_errorplotdata(self): """Returns empty error plot data.""" return [[], [], []] def get_lineplotdata(self): """Returns line plot data (not used for temperature).""" return [[], []] def get_xmin(self): """Returns minimum of x.""" xdata = self.get_xdata() return xdata.min() if len(xdata) > 0 else 0 def get_xmax(self): """Returns maximum of x.""" xdata = self.get_xdata() return xdata.max() if len(xdata) > 0 else 0 def get_ymin(self): """Returns minimum of y.""" ydata = self.get_ydata(0) return ydata.min() if len(ydata) > 0 else 0 def get_ymax(self): """Returns maximum of y.""" ydata = self.get_ydata(0) return ydata.max() if len(ydata) > 0 else 0 def get_xminpos(self): """Returns smallest positive value of x.""" xdata = self.get_xdata() mask = xdata > 0 if numpy.any(mask): return xdata[mask].min() return 0 def get_yminpos(self): """Returns smallest positive value of y.""" ydata = self.get_ydata(0) mask = ydata > 0 if numpy.any(mask): return ydata[mask].min() return 0 def __repr__(self): """String representation of the temperature result.""" return f"TemperatureResult(points={len(self)}, job_id={self.job_id})"