class Itc503StreamInterface(StreamInterface): # Commands that we expect via serial during normal operation commands = { CmdBuilder("set_p").escape("P").float().eos().build(), CmdBuilder("set_i").escape("I").float().eos().build(), CmdBuilder("set_d").escape("D").float().eos().build(), CmdBuilder("get_p").escape("R8").eos().build(), CmdBuilder("get_i").escape("R9").eos().build(), CmdBuilder("get_d").escape("R10").eos().build(), CmdBuilder("get_temp_1").escape("R1").eos().build(), CmdBuilder("get_temp_2").escape("R2").eos().build(), CmdBuilder("get_temp_3").escape("R3").eos().build(), CmdBuilder("get_temp_sp").escape("R0").eos().build(), CmdBuilder("set_temp").escape("T").float().eos().build(), CmdBuilder("get_status").escape("X").eos().build(), CmdBuilder("set_ctrl").escape("C").int().eos().build(), CmdBuilder("set_mode").escape("A").int().eos().build(), CmdBuilder("set_ctrl_chan").escape("H").int().eos().build(), CmdBuilder("set_autopid_on").escape("L1").eos().build(), CmdBuilder("set_autopid_off").escape("L0").eos().build(), CmdBuilder("set_heater_maxv").escape("M").float().eos().build(), # No readback for max heater output CmdBuilder("set_heater_voltage").escape("O").float().eos().build(), CmdBuilder("get_heater_voltage").escape("R6").eos().build(), CmdBuilder("get_heater_percent").escape("R5").eos().build(), CmdBuilder("get_temp_error").escape("R4").eos().build(), } in_terminator = "\r" out_terminator = "\r" def handle_error(self, request, error): err_string = "command was: {}, error was: {}: {}\n".format( request, error.__class__.__name__, error) print(err_string) self.log.error(err_string) return err_string def set_p(self, p): self.device.p = float(p) return "P" def get_p(self): return "R{:.1f}".format(self.device.p) def set_i(self, i): self.device.i = float(i) return "I" def get_i(self): return "R{:.1f}".format(self.device.i) def set_d(self, d): self.device.d = float(d) return "D" def get_d(self): return "R{:.1f}".format(self.device.d) def get_temp_1(self): return "R{:.1f}".format(self.device.temperature_1) def get_temp_2(self): return "R{:.1f}".format(self.device.temperature_2) def get_temp_3(self): return "R{:.1f}".format(self.device.temperature_3) def set_temp(self, temp): self.device.temperature_sp = float(temp) return "T" def get_temp_sp(self): return "R{:.1f}".format(self.device.temperature_sp) def get_status(self): if self.device.report_sweep_state_with_leading_zero: format_string = "X0A{mode}C{ctrl}S{sweeping:02d}H{control_channel}L{autopid}" else: format_string = "X0A{mode}C{ctrl}S{sweeping:01d}H{control_channel}L{autopid}" return format_string.format( mode=self.device.mode, ctrl=self.device.control, sweeping=1 if self.device.sweeping else 0, control_channel=self.device.control_channel, autopid=1 if self.device.autopid else 0) def set_ctrl(self, ctrl): self.device.control = int(ctrl) return "C" def set_mode(self, mode): self.device.mode = int(mode) return "A" def set_ctrl_chan(self, chan): if not 1 <= int(chan) <= 3: raise ValueError("Invalid channel") self.device.control_channel = int(chan) return "H" def set_autopid_on(self): self.device.autopid = True return "L" def set_autopid_off(self): self.device.autopid = False return "L" def set_heater_voltage(self, manv): self.device.heater_voltage = float(manv) return "O" def get_heater_voltage(self): return "R{:.1f}".format(self.device.heater_voltage) def get_heater_percent(self): return "R{:.1f}".format(self.device.heater_percent) def set_heater_maxv(self, volts): raise ValueError("At ISIS, do not use this command!") def get_temp_error(self): return "R{:.1f}".format( abs(self.device.temperature_sp - self.device.temperature))
class Danfysik8500StreamInterface(CommonStreamInterface, StreamInterface): """ Stream interface for a Danfysik model 8500. """ in_terminator = "\r" out_terminator = "\n\r" protocol = 'model8500' # This is the address of the LOQ danfysik 8500 PSU_ADDRESS = 75 commands = CommonStreamInterface.commands + [ CmdBuilder("set_current").escape("DA 0 ").int().eos().build(), CmdBuilder("get_current").escape("AD 8").eos().build(), CmdBuilder("set_address").escape("ADR ").int().eos().build(), CmdBuilder("get_address").escape("ADR").eos().build(), CmdBuilder("init_comms").escape("REM").eos().build(), CmdBuilder("init_comms").escape("UNLOCK").eos().build(), CmdBuilder("get_slew_rate").escape("R").arg( r"[1-3]", argument_mapping=int).eos().build(), CmdBuilder("set_slew_rate").escape("W").arg( r"[1-3]", argument_mapping=int).spaces().int().eos().build() ] @conditional_reply("device_available") @conditional_reply("comms_initialized") def get_status(self): """ Respond to the get_status command (S1) """ response = "{power_off}{pol_normal}{pol_reversed}{reg_transformer}{dac16}{dac17}{is_percent}{spare}"\ "{transistor_fault}{sum_interlock}{dc_overcurrent}{dc_overload}{reg_mod_fail}{prereg_fail}" \ "{phase_fail}{mps_waterflow_fail}{earth_leak_fail}{thermal_fail}{mps_overtemperature}" \ "{door_switch}{mag_waterflow_fail}{mag_overtemp}{mps_not_ready}{spare}".format( spare=self.bit(False), power_off=self.bit(not self.device.power), pol_normal=self.bit(not self.device.negative_polarity), pol_reversed=self.bit(self.device.negative_polarity), reg_transformer=self.bit(False), dac16=self.bit(False), dac17=self.bit(False), is_percent=self.bit(False), transistor_fault=self.interlock("transistor_fault"), sum_interlock=self.bit(len(self.device.active_interlocks) > 0), dc_overcurrent=self.interlock("dc_overcurrent"), dc_overload=self.interlock("dc_overload"), reg_mod_fail=self.interlock("reg_mod_fail"), prereg_fail=self.interlock("prereg_fail"), phase_fail=self.interlock("phase_fail"), mps_waterflow_fail=self.interlock("mps_waterflow_fail"), earth_leak_fail=self.interlock("earth_leak_fail"), thermal_fail=self.interlock("thermal_fail"), mps_overtemperature=self.interlock("mps_overtemperature"), door_switch=self.interlock("door_switch"), mag_waterflow_fail=self.interlock("mag_waterflow_fail"), mag_overtemp=self.interlock("mag_overtemp"), mps_not_ready=self.bit(not self.device.power), ) assert len( response) == 24, "length should have been 24 but was {}".format( len(response)) return response def set_address(self, value): self.device.set_address(value) @conditional_reply("comms_initialized") def get_address(self): return "{:03d}".format(self.address) @conditional_reply("comms_initialized") def get_slew_rate(self, dac_num): return self.device.get_slew_rate(dac_num) @conditional_reply("comms_initialized") def set_slew_rate(self, dac_num, slew_rate_value): self.device.set_slew_rate(dac_num, slew_rate_value)
class Keithley2001StreamInterface(StreamInterface): in_terminator = "\r\n" out_terminator = "\n" _channel_readback_format = None commands = { # Commands used on setup CmdBuilder("get_idn").escape("*IDN?").eos().build(), CmdBuilder("reset_device").escape("*RST").eos().build(), CmdBuilder("set_buffer_source").escape(":DATA:FEED ").arg( "NONE|SENS1|CALC1").eos().build(), CmdBuilder("get_buffer_source").escape(":DATA:FEED?").eos().build(), CmdBuilder("set_buffer_egroup").escape(":DATA:EGR ").arg( "FULL|COMP").eos().build(), CmdBuilder("get_buffer_egroup").escape(":DATA:EGR?").eos().build(), CmdBuilder("set_continuous_initialization").escape(":INIT:CONT ").arg( "OFF|ON").eos().build(), CmdBuilder("get_continuous_initialization_status").escape( ":INIT:CONT?").eos().build(), CmdBuilder("get_elements").escape(":FORM:ELEM?").eos().build(), CmdBuilder("set_elements").escape( ":FORM:ELEM ").string().eos().build(), CmdBuilder("get_measurement_status").escape( ":STAT:MEAS:ENAB?").eos().build(), CmdBuilder("set_buffer_full_status").escape( ":STAT:MEAS:ENAB 512").eos().build(), CmdBuilder("get_service_request_status").escape("*SRE?").eos().build(), CmdBuilder("set_measure_summary_status").escape( "*SRE 1").eos().build(), CmdBuilder("reset_and_clear_status_registers").escape( ":STAT:PRES; *CLS").eos().build(), CmdBuilder("set_scan_count").escape( ":ARM:LAY2:COUN ").int().eos().build(), CmdBuilder("get_scan_count").escape(":ARM:LAY2:COUN?").eos().build(), CmdBuilder("get_scan_trigger").escape(":ARM:LAY2:SOUR?").eos().build(), # Reading a single channel CmdBuilder("set_read_channel").escape(":ROUT:CLOS (@").int().escape( ")").eos().build(), CmdBuilder("read_single_channel").escape(":READ?").eos().build(), # Reading from the buffer CmdBuilder("set_buffer_mode").escape(":DATA:FEED:CONT ").arg( "NEV|NEXT|ALW|PRET").eos().build(), CmdBuilder("get_buffer_mode").escape(":DATA:FEED:CONT?").eos().build(), CmdBuilder("clear_buffer").escape(":DATA:CLE").eos().build(), CmdBuilder("scan_channels").escape(":INIT").eos().build(), CmdBuilder("get_buffer_date").escape(":DATA:DATA?").eos().build(), # Setting up a scan CmdBuilder("set_measurement_scan_count").escape(":TRIG:COUN " ).int().eos().build(), CmdBuilder("get_measurement_scan_count").escape( ":TRIG:COUN?").eos().build(), CmdBuilder("set_buffer_size").escape( ":DATA:POIN ").int().eos().build(), CmdBuilder("get_buffer_size").escape(":DATA:POIN?").eos().build(), CmdBuilder("set_scan_channels").escape(":ROUT:SCAN (@").arg( "[0-9,]+").escape(")").eos().build(), CmdBuilder("get_scan_channels").escape(":ROUT:SCAN?").eos().build(), # Error handling CmdBuilder("get_error").escape(":SYST:ERR?").eos().build() } def handle_error(self, request, error): self.log.error("An error occurred at request {}: {}".format( repr(request), repr(error))) print("An error occurred at request {}: {}".format( repr(request), repr(error))) # Commands used on setup @conditional_reply("_connected") def get_idn(self): """ Returns the devices IDN string. Returns: string: The device's IDN. """ idn = self._device.idn return idn @conditional_reply("_connected") def get_elements(self): """ Returns the lists of elements of a reading in alphabetical order from the device. """ elements = [ element for element, value in self._device.elements.items() if value ] return ", ".join(elements) @conditional_reply("_connected") def set_elements(self, string): """ Sets the elements a reading has. Args: string: String of comma separated elements of a reading. Valid elements are: READ, CHAN, RNUM, UNIT, TIME, STAT. """ elements = {element.strip().upper() for element in string.split(",")} for element in elements: try: self._device.elements[element] = True except LookupError: self.log.error( "Tried to set {} which is not a valid reading element.". format(element)) print("Tried to set {} which is not a valid reading element.". format(element)) self._generate_readback_format() @conditional_reply("_connected") def _generate_readback_format(self): """ Generates the readback format for buffer readings. """ readback_elements = [] if self._device.elements["READ"]: readback_elements.append("{:.7E}") if self._device.elements["UNIT"]: readback_elements.append("{}") if self._device.elements["CHAN"]: readback_elements.append(",{:02d}") if self._device.elements["UNIT"]: readback_elements.append("INTCHAN") self._channel_readback_format = "".join(readback_elements) @conditional_reply("_connected") def reset_device(self): """ Resets device. """ self._device.reset_device() @conditional_reply("_connected") def set_buffer_source(self, source): """ Sets the buffer source. """ self._device.buffer.source = source @conditional_reply("_connected") def get_buffer_source(self): """ Gets the buffer source. """ return self._device.buffer.source @conditional_reply("_connected") def set_buffer_egroup(self, egroup): """ Sets the buffer element group. """ self._device.number_of_times_ioc_has_been_reset += 1 self._device.buffer.egroup = egroup @conditional_reply("_connected") def get_buffer_egroup(self): """ Gets the buffer element group. """ return self._device.buffer.egroup @conditional_reply("_connected") def set_continuous_initialization(self, value): """ Sets continuous scanning status to ON or OFF. Thus is called continuous initialization mode in the Keithley 2001 manual. Args: value (string): ON or OFF. """ if value.upper() == "ON": self._device.continuous_initialisation_status = True elif value.upper() == "OFF": self._device.continuous_initialisation_status = False else: raise ValueError("Not a valid continuous initialisation mode") @conditional_reply("_connected") def get_continuous_initialization_status(self): """ Gets the continuous scanning status. Thus is the continuous initialization mode in the Keithley 2001 manual. """ status = "OFF" if self._device.continuous_initialisation_status: status = "ON" return status @conditional_reply("_connected") def set_buffer_full_status(self): """ Sets the buffer full status of the status register to true. """ self._device.status_register.buffer_full = True @conditional_reply("_connected") def set_measure_summary_status(self): """ Sets the measurement summary status of the status register to true. """ self._device.status_register.measurement_summary_status = True @conditional_reply("_connected") def get_measurement_status(self): """ Returns the measurement status of the device. Returns: string: integer which represents the measurement status register status in bits. """ status = 0 if self._device.status_register.buffer_full: status += 512 return str(status) @conditional_reply("_connected") def get_service_request_status(self): """ Returns the measurement status of the device. Returns: string: integer which represents the service register status in bits. """ status = 0 if self._device.status_register.measurement_summary_status: status += 1 return str(status) @conditional_reply("_connected") def reset_and_clear_status_registers(self): """ Resets and clears the status registers of the device. """ self._device.status_register.reset_and_clear() @conditional_reply("_connected") def set_scan_count(self, value): """ Sets the scan count. Args: value (int): Number of times to scan. """ self._device.scan_count = int(value) @conditional_reply("_connected") def get_scan_count(self): """ Returns the number of times the device is set to scan. Returns: string: Number of times the device is set to scan. """ return str(self._device.scan_count) @conditional_reply("_connected") def get_scan_trigger(self): """ Returns the scan trigger type. Returns: string: Scan trigger mode. One of IMM, HOLD, MAN, BUS, TLINK, EXT, TIM. """ return self._device.scan_trigger_type # Reading a single channel @conditional_reply("_connected") def set_read_channel(self, channel): """ Sets the channels to read from in single read mode. Args: channel string): String representation of a channel number between 1 and 10. """ self._device.close_channel(int(channel)) @conditional_reply("_connected") def read_single_channel(self): """ Takes a single reading from the closed channel on the device. Returns: string: Formatted string of channel data. """ channel_data = self._device.take_single_reading() return "".join(self._format_buffer_readings(channel_data)) # Setting up for a scan @conditional_reply("_connected") def set_buffer_mode(self, mode): """ Sets the buffer mode. """ self._device.buffer.mode = mode @conditional_reply("_connected") def get_buffer_mode(self): """ Gets the buffer mode. """ return self._device.buffer.mode @conditional_reply("_connected") def set_buffer_size(self, size): """ Sets the buffer mode. """ self._device.buffer.size = int(size) @conditional_reply("_connected") def get_buffer_size(self): """ Gets the buffer mode. """ return self._device.buffer.size @conditional_reply("_connected") def clear_buffer(self): """ Clears the buffer. """ self._device.buffer.clear_buffer() @conditional_reply("_connected") def set_scan_channels(self, channels): """ Sets the channels to scan. Args: channels (string): Comma separated list of channel number to read from. """ channels = channels.split(",") self._device.buffer.scan_channels = channels @conditional_reply("_connected") def get_scan_channels(self): """ Returns the channels set to scan. Returns: string: comman separated list of channels set to scan """ return "(@" + ",".join(self._device.buffer.scan_channels) + ")" @conditional_reply("_connected") def set_measurement_scan_count(self, value): """ Sets the measurement scan count. Args: value (int): Number of times to trigger measurements. """ self._device.measurement_scan_count = int(value) @conditional_reply("_connected") def get_measurement_scan_count(self): """ Gets the measurement scan count. Returns: value (int): Number of times to trigger measurements """ return str(self._device.measurement_scan_count) @conditional_reply("_connected") def scan_channels(self): """ Sets the device to scan. """ self._device.scan_channels() @conditional_reply("_connected") def get_buffer_date(self): """ Returns the buffer data. Returns: list of strings: List of readings from channels. """ return ",".join( map(self._format_buffer_readings, self._device.buffer.buffer)) def _format_buffer_readings(self, reading): """ Formats a reading. Args: reading: dictionary with keys "READ", "READ_UNIT", "CHAN" Returns: string: Buffer reading formatted depending on elements """ formatted_buffer_reading = [] if self._device.elements["READ"]: formatted_buffer_reading.append(reading["READ"]) if self._device.elements["UNIT"]: formatted_buffer_reading.append(reading["READ_UNIT"]) if self._device.elements["CHAN"]: formatted_buffer_reading.append(reading["CHAN"]) return self._channel_readback_format.format(*formatted_buffer_reading) @conditional_reply("_connected") def get_error(self): """ Returns the error code and message. Returns: (string): Returns the error string formed of error code, error message. """ return ",".join( ["{}".format(self._device.error[0]), self._device.error[1]])
class KepcoStreamInterface(StreamInterface): in_terminator = "\r\n" out_terminator = "\r\n" commands = { CmdBuilder("write_voltage").escape("VOLT ").float().build(), CmdBuilder("read_actual_voltage").escape("MEAS:VOLT?").build(), CmdBuilder("read_actual_current").escape("MEAS:CURR?").build(), CmdBuilder("write_current").escape("CURR ").float().build(), CmdBuilder("read_setpoint_voltage").escape("VOLT?").build(), CmdBuilder("read_setpoint_current").escape("CURR?").build(), CmdBuilder("set_output_mode").escape("FUNC:MODE ").arg("VOLT|CURR").build(), CmdBuilder("read_output_mode").escape("FUNC:MODE?").build(), CmdBuilder("read_output_status").escape("OUTP?").build(), CmdBuilder("set_output_status").escape("OUTP ").arg("0|1").build(), CmdBuilder("get_IDN").escape("*IDN?").build(), CmdBuilder("set_control_mode").escape("SYST:REM ").arg("0|1").build(), CmdBuilder("reset").escape("*RST").build() } def handle_error(self,request, error): self.log.error("An error occurred at request" + repr(request) + ": " + repr(error)) print("An error occurred at request" + repr(request) + ": " + repr(error)) @if_connected def read_actual_voltage(self): return "{0}".format(self._device.voltage) @if_connected def read_actual_current(self): return "{0}".format(self._device.current) @if_connected @needs_remote_mode def write_voltage(self, voltage): self._device.setpoint_voltage = voltage @if_connected @needs_remote_mode def write_current(self, current): self._device.setpoint_current = current @if_connected def read_setpoint_voltage(self): return "{0}".format(self._device.setpoint_voltage) @if_connected def read_setpoint_current(self): return "{0}".format(self._device.setpoint_current) @if_connected @needs_remote_mode def set_output_mode(self, mode): self._device.output_mode = 0 if mode.startswith("CURR") else 1 @if_connected def read_output_mode(self): return "{0}".format(self._device.output_mode) @if_connected @needs_remote_mode def set_output_status(self, status): self._device.output_status = status @if_connected def read_output_status(self): return "{0}".format(self._device.output_status) @if_connected def get_IDN(self): return "{0}".format(self._device.idn) @if_connected def set_control_mode(self, mode): if self._device.firmware <= 2.0: raise ValueError("No SYST:REM command available") else: mode = int(mode) if mode not in [0, 1]: raise ValueError("Invalid mode in set_control_mode: {}".format(mode)) self._device.remote_comms_enabled = (mode == 1) @if_connected def reset(self): self._device.reset()