"""Thermal chamber SCPI driver. This module implements a client-side driver for thermal chambers that communicate via SCPI commands. """ import time from py_dvt_ate.instruments.drivers.base import BaseDriver from py_dvt_ate.instruments.interfaces import IThermalChamber class ThermalChamberDriver(BaseDriver, IThermalChamber): """SCPI driver for thermal chambers. Provides high-level Python API for controlling thermal chambers via SCPI commands. Implements the IThermalChamber interface. SCPI Commands Used: TEMP:SETPOINT - Set target temperature (°C) TEMP:SETPOINT? - Query current setpoint TEMP:ACTUAL? - Query actual chamber temperature TEMP:STAB? - Query stability (1=stable, 0=settling) TEMP:RAMP - Set temperature ramp rate (°C/min) TEMP:RAMP? - Query ramp rate Example: >>> transport = TCPTransport("localhost", 5001) >>> chamber = ThermalChamberDriver(transport) >>> chamber.connect() >>> chamber.set_temperature(85.0) >>> chamber.wait_until_stable(timeout=600.0) >>> temp = chamber.get_temperature() """ def set_temperature(self, setpoint: float) -> None: """Set the chamber temperature setpoint. Args: setpoint: Target temperature in degrees Celsius. Raises: ConnectionError: If not connected. IOError: If command fails. """ self.write(f"TEMP:SETPOINT {setpoint:.2f}") def get_temperature(self) -> float: """Get the actual chamber temperature. Returns: Current chamber temperature in degrees Celsius. Raises: ConnectionError: If not connected. IOError: If query fails. """ return self.query_float("TEMP:ACTUAL?") def get_setpoint(self) -> float: """Get the current temperature setpoint. Returns: Current setpoint in degrees Celsius. Raises: ConnectionError: If not connected. IOError: If query fails. """ return self.query_float("TEMP:SETPOINT?") def is_stable(self) -> bool: """Check if chamber temperature is stable. Temperature is considered stable when it has settled within the instrument's configured stability threshold of the setpoint. Returns: True if temperature is stable, False if still settling. Raises: ConnectionError: If not connected. IOError: If query fails. """ return self.query_bool("TEMP:STAB?") def wait_until_stable( self, timeout: float = 300.0, poll_interval: float = 1.0 ) -> bool: """Wait until chamber temperature stabilises. Polls the stability status at regular intervals until stable or timeout is reached. Args: timeout: Maximum time to wait in seconds. Default 300s (5 minutes). poll_interval: Time between stability checks in seconds. Default 1s. Returns: True if temperature stabilised within timeout, False if timed out. Raises: ConnectionError: If not connected. IOError: If communication fails. ValueError: If timeout or poll_interval are negative. """ if timeout < 0: raise ValueError("Timeout must be non-negative") if poll_interval <= 0: raise ValueError("Poll interval must be positive") start_time = time.time() while time.time() - start_time < timeout: if self.is_stable(): return True time.sleep(poll_interval) return False def set_ramp_rate(self, rate: float) -> None: """Set the temperature ramp rate. Args: rate: Ramp rate in degrees Celsius per minute. Raises: ConnectionError: If not connected. IOError: If command fails. """ self.write(f"TEMP:RAMP {rate:.2f}") def get_ramp_rate(self) -> float: """Get the current temperature ramp rate. Returns: Ramp rate in degrees Celsius per minute. Raises: ConnectionError: If not connected. IOError: If query fails. """ return self.query_float("TEMP:RAMP?")