Source code for lab_driver.dmm6500

from time import sleep
from logging import getLogger, Logger
import pyvisa
from lab_driver.scan_instruments import scan_instruments


[docs] class DriverDMM6500: SerialDevice: pyvisa.Resource SerialActive = False _device_name_chck = "DMM6500" _logger: Logger def __init__(self): """Class for handling the Keithley Digital Multimeter 6500 in Python""" self._logger = getLogger(__name__) def __write_to_dev(self, order: str) -> None: self.SerialDevice.write(order) def __read_from_dev(self, order: str) -> str: num_stop = 100 num_trials = 0 while (num_trials < num_stop): try: text_out = self.SerialDevice.query(order) num_trials = num_stop except: sleep(0.1) num_trials += 1 return text_out.strip() def __init_dev(self, do_reset: bool=True, do_beep: bool=True) -> None: """Function for initialisation of DAQ device :param do_reset: Reset the DAQ device :param do_beep: Do a beep on DAQ device after init done :return: None """ self.__write_to_dev("*LANG SCPI") if self.SerialActive: if do_reset: self.do_reset() if do_beep: self.do_beep() self._logger.debug(f"Right device is selected with: {self.get_id()}") else: self._logger.debug("Not right selected device. Please check!") def __do_check_idn(self) -> None: """Checking the IDN""" id_back = self.get_id() self.SerialActive = self._device_name_chck in id_back
[docs] def serial_start_known_target(self, resource_name: str, do_reset: bool=False) -> None: """Open the serial connection to device directly""" rm = pyvisa.ResourceManager() self.SerialDevice = rm.open_resource(resource_name) self.__do_check_idn() self.__init_dev(do_reset)
[docs] def serial_start(self, do_reset: bool=False, do_beep: bool=True) -> None: """Open the serial connection to device :param do_reset: Reset the DAQ device :param do_beep: Do a beep on DAQ device after init done :return: None """ list_dev = scan_instruments() rm = pyvisa.ResourceManager("@py") # --- Checking if device address is right for inst_name in list_dev: self.SerialDevice = rm.open_resource(inst_name) self.__do_check_idn() if self.SerialActive: break else: self.serial_close() # --- Init of device self.__init_dev(do_reset, do_beep)
[docs] def serial_close(self) -> None: """Closing the serial connection""" self.SerialDevice.close() self.SerialActive = False
[docs] def get_id(self) -> str: """Getting the device ID""" id = self.__read_from_dev("*IDN?") self._logger.debug(f"Device ID: {id}") return id
[docs] def do_reset(self) -> None: """Reset the device""" if not self.SerialActive: self._logger.debug("... RST not done due to wrong device") else: self.__write_to_dev("*RST") sleep(1)
[docs] def do_beep(self) -> None: """Doing a beep signal""" time_sleep = 0.5 self.__write_to_dev(f':SYST:BEEP 300, {time_sleep}') sleep(time_sleep * 1.1)
[docs] def set_measurement_mode(self, mode: str, polarity: str = "") -> None: """Set the measurement mode Args: mode: "VOLT", "CURR", "RES" or "FRES" polarity: "DC" or "AC" applicable for current and voltage, else "" Returns: None """ if polarity: self.__write_to_dev(f':SENS:FUNC "{mode}:{polarity}"') else: self.__write_to_dev(f':SENS:FUNC "{mode}"')
[docs] def read_value(self) -> float: """Reading value from display""" if not self.SerialActive: self._logger.debug("... reading not done due to wrong device") else: return float(self.__read_from_dev(":READ?"))
def __float_eq(self, x, y, epsilon=0.00000001): return abs(x - y) < epsilon
[docs] def get_voltage(self): """Get voltage reading Args: N/A Returns: Voltage in Volts """ return float(self.__read_from_dev(":MEAS:VOLT?"))
[docs] def get_current(self): """Get current reading Args: N/A Returns: Current in Ampere """ return float(self.__read_from_dev(":MEAS:CURR?"))
[docs] def get_2wire_resistance(self): """Get 2-wire resistance reading Args: N/A Returns: 2-wire resistance in Ohms """ return float(self.__read_from_dev(":MEAS:RES?"))
[docs] def get_4wire_resistance(self): """Get 4-wire resistance reading Args: N/A Returns: 4-wire resistance in Ohms """ return float(self.__read_from_dev(":MEAS:FRES?"))
# NOTE: manual page 498 def __set_volt_curr_range(self, function: str, polarity: str, range: float): """Wrapper for changing ranges of voltage or current readings Args: function: "VOLT" or "CURR" polarity: "AC" or "DC" range: One of the available ranges for that selection Returns: True on failure """ available_ranges = { "DC" : { "VOLT": ["1e-1", "1", "10", "100", "1000"], "CURR": ["1e-5", "1e-4", "1e-3", "1e-2", "1e-1", "1", "3"] }, "AC" : { "VOLT": ["1e-1", "1", "10", "100", "750"], "CURR": ["1e-3", "1e-2", "1e-1", "1", "3"] } } if self.__read_from_dev(":ROUT:TERM?") == "REAR": available_ranges["DC"]["CURR"].append("10") available_ranges["AC"]["CURR"].append("10") if polarity in available_ranges and function in available_ranges[polarity]: ranges = available_ranges[polarity][function] else: self._logger.info("Changing measurement range failed. Check polarity. Check function.") return True for x in ranges: if self.__float_eq(float(x), range): self.__write_to_dev(f":SENS:{function}:{polarity}:RANG {x}") return False self._logger.info("Changing measurement range failed. Check range selection.") return True
[docs] def set_4wire_offset_compensation(self, compensation: bool | str) -> bool: """Enable or disable offset compensation for 4-wire resistance Args: compensation: True to enable, False to disable, "AUTO" to enable automatically Returns: True on failure; invalid argument """ if compensation == "AUTO": self.__write_to_dev(":SENS:FRES:OCOM AUTO") elif type(compensation) == bool: self.__write_to_dev(f":SENS:FRES:OCOM {int(compensation)}") else: self._logger.debug("Setting 4-wire resistance offset compensation failed. Check argument.") return True return False
def __set_resistance_range(self, range: int | str, type: int) -> bool: """Wrapper for changing ranges of resistance readings Args: range: One of the available ranges for that type of resistance reading or "AUTO". All ranges are powers of 10. 2-wire ranges are 10^1 to 10^8. 4-wire ranges are 10^0 to 10^8 if offset compensation is off or auto. 4-wire ranges are 10^0 to 10^4 if offset compensation is on. type: 2 or 4 to change resistance range of 2-wire of 4-wire resistance. Returns: True on failure """ if range == "AUTO": if type not in (2,4): self._logger.info(f"Only 2-wire and 4-wire resistance types are supported. You selected {type}.") return True self.__write_to_dev(":SENS:#RES:RANG:AUTO ON".replace('#', 'F' if type == 4 else '')) return False try: from math import log10 power = log10(range) if power != int(power): self._logger.info("Range argument must be power of 10.") return True except: self._logger.info(f"Mathematical error during computation of log10 of range argument: {range}.") return True if type == 2: if 1 <= power <= 8: self.__write_to_dev(f":SENS:RES:RANG {range}") return False elif type == 4: offset_comp = self.__read_from_dev(":SENS:FRES:OCOM?") valid = offset_comp == "ON" and 0 <= power <= 4 valid |= offset_comp in ("OFF", "AUTO") and 0 <= power <= 8 if valid: self.__write_to_dev(f":SENS:FRES:RANG {range}") return False else: self._logger.info(f"Only 2-wire and 4-wire resistance types are supported. You selected {type}.") return True self._logger.info(f"Range argument is out of supported range: {range}") return True
[docs] def set_2wire_resistance_range(self, range: int | str) -> bool: """Set measurement range of 2-wire resistance Args: range: Power of 10 from 10^1 to 10^8 or "AUTO" Returns: True on failure """ return self.__set_resistance_range(range, 2)
[docs] def set_4wire_resistance_range(self, range: int | str) -> bool: """Set measurement range of 4-wire resistance Args: range: Power of 10 from 10^0 to 10^8 if offset compensation is off or "AUTO", else 10^0 to 10^4 if offset compensation is on. Returns: True on failure """ return self.__set_resistance_range(range, 4)
[docs] def set_voltage_range(self, range: float | str, polarity: str = "DC") -> bool: """Set measurement range of voltage Args: range: Available ranges are 0.1, 1, 10, 100 and 1000 Volts in DC mode and 0.1, 1, 10, 100, 750 in AC mode or just "AUTO" polarity: "DC" or "AC", default is "DC" Returns: True on failure """ if range == "AUTO": self.__write_to_dev(":SENS:VOLT:RANG:AUTO ON") return False else: return self.__set_volt_curr_range("VOLT", polarity, range)
[docs] def set_current_range(self, range: float | str, polarity: str = "DC") -> bool: """Set measurement range of current Args: range: Available ranges are 0.00001, 0.0001, 0.001, 0.01, 0.1, 1 and 3 Amps in DC mode and 0.001, 0.01, 0.1, 1 and 3 Amps in AC mode or just "AUTO". When the rear terminals are used, 10 Amp range is available. polarity: "DC" or "AC", default is "DC" Returns: True on failure """ if range == "AUTO": self.__write_to_dev(":SENS:CURR:RANG:AUTO ON") return False else: return self.__set_volt_curr_range("CURR", polarity, range)