From 84920b8b322a8c79dcc4548cb7188126766bdfbf Mon Sep 17 00:00:00 2001 From: Maximilian Mucha Date: Wed, 13 Nov 2024 16:17:59 +0100 Subject: [PATCH 1/7] ENH: refactored code (changed property getter/setter to normal functions for compatibility reasons) added get_current_trip* commands for different ampere ranges --- basil/HL/iseg_hv.py | 649 +++++++++++++++++++++++++++++--------------- 1 file changed, 424 insertions(+), 225 deletions(-) diff --git a/basil/HL/iseg_hv.py b/basil/HL/iseg_hv.py index a8091a91..7e362d38 100644 --- a/basil/HL/iseg_hv.py +++ b/basil/HL/iseg_hv.py @@ -21,85 +21,102 @@ class IsegHV(HardwareLayer): # Command references from protocol CMDS = { - 'get_identifier': '#', - 'set_answer_delay': 'W={value}', - 'get_answer_delay': 'W', - 'get_voltage_meas': 'U{channel}', - 'get_current_meas': 'I{channel}', - 'get_v_lim': 'M{channel}', - 'get_i_lim': 'N{channel}', - 'get_voltage_set': 'D{channel}', - 'set_voltage': 'D{channel}={value}', - 'get_ramp_speed': 'V{channel}', - 'set_ramp_speed': 'V{channel}={value}', - 'start_voltage_ramp': 'G{channel}', - 'set_current_trip': 'L{channel}={value}', - 'get_current_trip': 'L{channel}', - 'get_status_word': 'S{channel}', - 'get_module_status': 'T{channel}', - 'set_autostart': 'A{channel}={value}', - 'get_autostart': 'A{channel}' + "get_identifier": "#", + "set_answer_delay": "W={value}", + "get_answer_delay": "W", + "get_voltage": "U{channel}", + "get_current": "I{channel}", + "get_voltage_limit": "M{channel}", + "get_current_limit": "N{channel}", + "get_source_voltage": "D{channel}", + "set_voltage": "D{channel}={value}", + "get_ramp_speed": "V{channel}", + "set_ramp_speed": "V{channel}={value}", + "start_voltage_ramp": "G{channel}", + "set_current_trip": "L{channel}={value}", # Same functionality as set_current_trip_mA + "get_current_trip": "L{channel}", # Same functionality as get_current_trip_mA + "set_current_trip_mA": "LB{channel}={value}", + "get_current_trip_mA": "LB{channel}", + "set_current_trip_muA": "LS{channel}={value}", + "get_current_trip_muA": "LS{channel}", + "get_status_word": "S{channel}", + "get_module_status": "T{channel}", + "set_autostart": "A{channel}={value}", + "get_autostart": "A{channel}", } FORMATS = { - 'get_voltage_meas': lambda val: f'{val[:-3]}e{val[-3:]}', - 'get_current_meas': lambda val: f'{val[:-3]}e{val[-3:]}', - 'get_voltage_set': lambda val: f'{val[:-3]}e{val[-3:]}', - 'get_current_trip': lambda val: f'{val[:-3]}e{val[-3:]}' + "get_voltage_meas": lambda val: f"{val[:-3]}e{val[-3:]}", + "get_current_meas": lambda val: f"{val[:-3]}e{val[-3:]}", + "get_voltage_set": lambda val: f"{val[:-3]}e{val[-3:]}", + "get_current_trip": lambda val: f"{val[:-3]}e{val[-3:]}", } ERRORS = { - '????': 'Syntax error in command', - '?WCN': 'Wrong channel number', - '?TOT': 'Timeout error (Unit will re-initialise)' + "????": "Syntax error in command", + "?WCN": "Wrong channel number", + "?TOT": "Timeout error (Unit will re-initialise)", } STATUS = { - 'ON': "Output voltage according to set voltage", - 'OFF': "Channel front panel switch off", - 'MAN': "Channel is on, set to manual mode", - 'ERR': "V_MAX or I_MAX was exceeded", - 'INH': "Inhibit signal was / is active", - 'QUA': "Quality of output voltage no guaranteed at present", - 'L2H': "Output voltage increasing", - 'H2L': "Output voltage decreasing", - 'LAS': "Look at status (only after G-command)", - 'TRP': "Current trip was active" + "ON": "Output voltage according to set voltage", + "OFF": "Channel front panel switch off", + "MAN": "Channel is on, set to manual mode", + "ERR": "V_MAX or I_MAX was exceeded", + "INH": "Inhibit signal was / is active", + "QUA": "Quality of output voltage no guaranteed at present", + "L2H": "Output voltage increasing", + "H2L": "Output voltage decreasing", + "LAS": "Look at status (only after G-command)", + "TRP": "Current trip was active", } - @property - def identifier(self): - """ - Read module identifier; return format is "UNIT_NUMBER;SOFTWARE_REL;V_MAX;I_MAX" + def __init__(self, intf, conf): + super(IsegHV, self).__init__(intf, conf) - Returns - ------- - str - Module identifier - """ - return self._get_set_property(prop='get_identifier') + # Store current channel number; default to channel 1 + self._channel = None - @property - def answer_delay(self): + # Store number of channels + self.n_channel = self._init.get("n_channel", 1) + self.channel = self._init.get("channel", 1) + + # Voltage which is considered the high voltage + self.high_voltage = self._init.get("high_voltage", None) + + # Software-side voltage limit, set via self.v_lim property + self._voltage_limit = self._init.get("v_lim", None) + + if self._voltage_limit is not None: + self.voltage_limit = self._init.get("voltage_limit", None) + + def init(self): + """Set up the power supply""" + # self.write("") # Synchronize: sometimze needed + + # Important: The manual states that the default answer delay is 3 ms. + # When querying the answer_delay property, it returns sometimes 0 although only values in between 1 and 255 ms are valid. + # Queries take very long which leads to serial timeouts. I suspect the default value on firmware side is in fact 255 ms (not 3 ms). + # Therefore, setting the answer_delay as first thing in the __init__ is sometimes required + self.answer_delay = 1 # ms + + # # Add error response for attempting to set voltage too high + self.ERRORS[f"? UMAX={self.get_voltage_limit}"] = ( + "Set voltage exceeds voltage limit" + ) + + def get_current(self): """ - answer delay (better 'delay') in between two output characters from the power supply in milliseconds. - Valid values are 1 up to and including 255 ms + Actual current flowing at output of self.channel in A Returns ------- - int - answer delay in ms (uint8) + float + Output current in A """ - return int(self._get_set_property(prop='get_answer_delay')) - - @answer_delay.setter - def answer_delay(self, bt): - if not 1 <= bt <= 255: - raise ValueError("answer delay must be 1 <= answer_delay <= 255 ms") - self._get_set_property(prop='set_answer_delay', value=bt) + return float(self._get_set_property(prop="get_current_meas")) - @property - def voltage(self): + def get_voltage(self): """ Actual voltage at self.channel output in V @@ -108,37 +125,47 @@ def voltage(self): float Output voltage in V """ - return float(self._get_set_property(prop='get_voltage_meas')) + return float(self._get_set_property(prop="get_voltage_meas")) - @voltage.setter - def voltage(self, voltage): + def set_voltage(self, voltage): # Check hardware voltage limit if abs(voltage) > abs(self.voltage_limit): - raise ValueError(f"Voltage of {voltage}V too high! Maximum voltage is {self.voltage_limit} V") + raise ValueError( + f"Voltage of {voltage}V too high! Maximum voltage is {self.voltage_limit} V" + ) # Get module status for checks - ms = self.module_status + ms = self.get_module_status() # Issue warning if PSU is in different polarity as value - if ms[5] == '1' and voltage < 0: - raise ValueError(f"Power supply polarity is set to positive but target voltage of {voltage}V is negative!") - elif ms[5] == '0' and voltage > 0: - raise ValueError(f"Power supply polarity is set to negative but target voltage of {voltage}V is positive!") + if ms[5] == "1" and voltage < 0: + raise ValueError( + f"Power supply polarity is set to positive but target voltage of {voltage}V is negative!" + ) + elif ms[5] == "0" and voltage > 0: + raise ValueError( + f"Power supply polarity is set to negative but target voltage of {voltage}V is positive!" + ) # Check software voltage limit - if self._v_lim is not None: - if (ms[5] == '1' and voltage > self._v_lim) or (ms[5] == '0' and voltage < self._v_lim): - raise ValueError(f"Voltage of {voltage}V too high! Increase *v_lim={self._v_lim}V* to enable higher voltage!") + if self._voltage_limit is not None: + if (ms[5] == "1" and voltage > self._voltage_limit) or ( + ms[5] == "0" and voltage < self._voltage_limit + ): + raise ValueError( + f"Voltage of {voltage}V too high! Increase *v_lim={self._v_lim}V* to enable higher voltage!" + ) # Issue warning if PSU is in manual mode # Then voltage can only be changed manually, at the device - if ms[6] == '1': - logging.warning("Power supply in manual mode; voltage changes have no effect!") + if ms[6] == "1": + logging.warning( + "Power supply in manual mode; voltage changes have no effect!" + ) - self._get_set_property(prop='set_voltage', value=abs(voltage)) + self._get_set_property(prop="set_voltage", value=abs(voltage)) - @property - def voltage_target(self): + def get_source_voltage(self): """ Target voltage of self.channel output in V This is the voltage which is set using self.voltage property @@ -148,22 +175,25 @@ def voltage_target(self): float Target voltage in V """ - return float(self._get_set_property(prop='get_voltage_set')) + return float(self._get_set_property(prop="get_source_voltage")) - @property - def current(self): + def get_hardware_voltage_limit(self): """ - Actual current flowing at output of self.channel in A + Return current hardware-side voltage limit of self.channel in V Returns ------- float - Output current in A + Voltage limit in V """ - return float(self._get_set_property(prop='get_current_meas')) + # Property get_v_lim returns voltage limit as percentage of max voltage + return ( + int(self._get_set_property(prop="get_voltage_limit")) + / 100.0 + * float(self.V_MAX[:-1]) + ) - @property - def v_lim(self): + def get_voltage_limit(self): """ Software-side voltage limit, initially None. If set, a check will happen before each voltage change @@ -172,40 +202,74 @@ def v_lim(self): float, None Software-side voltage limit """ - return self._v_lim + return self._voltage_limit - @v_lim.setter - def v_lim(self, voltage_limit): - self._v_lim = voltage_limit if voltage_limit is None else float(voltage_limit) + def set_voltage_limit(self, voltage_limit): + self._voltage_limit = ( + voltage_limit if voltage_limit is None else float(voltage_limit) + ) - @property - def voltage_limit(self): + def get_current_limit(self): """ - Return current hardware-side voltage limit of self.channel in V + Return current limit of self.channel in A Returns ------- float - Voltage limit in V + Current limit in A """ - # Property get_v_lim returns voltage limit as percentage of max voltage - return int(self._get_set_property(prop='get_v_lim')) / 100.0 * float(self.V_MAX[:-1]) + # Property get_i_lim returns voltage limit as percentage of max current + return ( + int(self._get_set_property(prop="get_current_limit")) / 100.0 * float(self.I_MAX[:-2]) + ) - @property - def current_limit(self): + def set_current_trip(self, resolution="mA"): """ - Return current limit of self.channel in A + Read current trip/limit, if 0 -> no trip Returns ------- float - Current limit in A + Current trip """ - # Property get_i_lim returns voltage limit as percentage of max current - return int(self._get_set_property(prop='get_i_lim')) / 100.0 * float(self.I_MAX[:-2]) + if resolution == "muA": + return float(self._get_set_property(prop="get_current_trip_muA")) + else: + return float(self._get_set_property(prop="get_current_trip_mA")) - @property - def ramp_speed(self): + def set_current_trip(self, ct, resolution="mA"): + if resolution == "muA": + self._get_set_property(prop="set_current_trip_muA", value=ct) + else: + self._get_set_property(prop="set_current_trip_mA", value=ct) + + def start_voltage_ramp(self): + """ + Manually initiate the change of the voltage. + Only needed if self.autostart is False + """ + self._get_set_property(prop="start_voltage_ramp") + + def get_current_channel(self): + return self._channel + + def set_current_channel(self, ch): + if not 1 <= ch <= self.n_channel: + raise ValueError(f"Channel number must be 1 <= channel <= {self.n_channel}") + self._channel = ch + + def get_identifier(self): + """ + Read module identifier; return format is "UNIT_NUMBER;SOFTWARE_REL;V_MAX;I_MAX" + + Returns + ------- + str + Module identifier + """ + return self._get_set_property(prop="get_identifier") + + def get_ramp_speed(self): """ Read the output voltage ramping speed in V/s. Valid values are 1 up to and including 255 V/s @@ -215,32 +279,51 @@ def ramp_speed(self): int Ramp speed of output voltage """ - return int(self._get_set_property(prop='get_ramp_speed')) + return int(self._get_set_property(prop="get_ramp_speed")) - @ramp_speed.setter - def ramp_speed(self, rs): + def set_ramp_speed(self, rs): if not 1 <= rs <= 255: raise ValueError("Ramp speed must be 1 <= ramp_speed <= 255 V/s") - self._get_set_property(prop='set_ramp_speed', value=rs) + self._get_set_property(prop="set_ramp_speed", value=rs) - @property - def current_trip(self): + def get_answer_delay(self): """ - Read current trip, if 0 -> no trip + answer delay (better 'delay') in between two output characters from the power supply in milliseconds. + Valid values are 1 up to and including 255 ms Returns ------- - float - Current trip + int + answer delay in ms (uint8) """ - return float(self._get_set_property(prop='get_current_trip')) + return int(self._get_set_property(prop="get_answer_delay")) - @current_trip.setter - def current_trip(self, ct): - self._get_set_property(prop='set_current_trip', value=ct) + def set_answer_delay(self, bt): + if not 1 <= bt <= 255: + raise ValueError("answer delay must be 1 <= answer_delay <= 255 ms") + self._get_set_property(prop="set_answer_delay", value=bt) - @property - def status_word(self): + def get_autostart(self): + """ + Whether output voltage changes automatically after setting value via self.voltage property. + Alternatively, call self.start_voltage_ramp method to initaite voltagte change manually. + self._get_set_property(prop='get_autostart') -> 008: autostart is active + self._get_set_property(prop='get_autostart') -> 000: autostart is inactive + + Returns + ------- + bool + Wheter autostart is active + """ + return self._get_set_property(prop="get_autostart") == "008" + + def set_autostart(self, state): + self._get_set_property(prop="set_autostart", value=8 if state else 0) + + def get_polarity(self): + return 1 if self.module_status[5] == "1" else -1 + + def get_status_word(self): """ Read status word of self.channel See self.STATUS and self.status_description @@ -250,10 +333,9 @@ def status_word(self): str Status word """ - return self._get_set_property(prop='get_status_word') + return self._get_set_property(prop="get_status_word") - @property - def status_description(self): + def get_status_description(self): """ Read description corresponding to self.status_word @@ -266,10 +348,9 @@ def status_description(self): for status in self.STATUS: if status in status_word: return f"{status_word}: {self.STATUS[status]}" - return 'No description' + return "No description" - @property - def module_status(self): + def get_module_status(self): """ Read module status of self.channel Return value is uint8, use '{:08b}'.format(value) to get bits @@ -279,10 +360,9 @@ def module_status(self): int Value of status """ - return '{:08b}'.format(int(self._get_set_property(prop='get_module_status'))) + return "{:08b}".format(int(self._get_set_property(prop="get_module_status"))) - @property - def module_description(self): + def get_module_description(self): """ Read module status and print out desciptive string from it @@ -291,107 +371,37 @@ def module_description(self): str Module description string """ - def module_msg(bit, prefix, t_msg, f_msg=''): - return f'{prefix} ' + (t_msg if bit == '1' else f_msg) + '\n' + + def module_msg(bit, prefix, t_msg, f_msg=""): + return f"{prefix} " + (t_msg if bit == "1" else f_msg) + "\n" _description = { - 0: dict(prefix='', t_msg="Quality of output voltage not given at present"), - 1: dict(prefix='', t_msg="V_MAX or I_MAX is / was exceeded"), - 2: dict(prefix='INHIBIT signal', t_msg="is / was active", f_msg="inactive"), + 0: dict(prefix="", t_msg="Quality of output voltage not given at present"), + 1: dict(prefix="", t_msg="V_MAX or I_MAX is / was exceeded"), + 2: dict(prefix="INHIBIT signal", t_msg="is / was active", f_msg="inactive"), 3: dict(prefix="KILL_ENABLE is", t_msg="on", f_msg="off"), 4: dict(prefix="Front-panel HV-ON switch is", t_msg="OFF", f_msg="ON"), 5: dict(prefix="Polarity set to", t_msg="positive", f_msg="negative"), 6: dict(prefix="Control via", t_msg="manual", f_msg="RS-232 interface"), - 7: dict(prefix="Display dialled to", t_msg="voltage measurement", f_msg="current measurement") + 7: dict( + prefix="Display dialled to", + t_msg="voltage measurement", + f_msg="current measurement", + ), } - module_description = '' + module_description = "" for i, bit in enumerate(self.module_status): module_description += module_msg(bit=bit, **_description[i]) return module_description - @property - def autostart(self): - """ - Whether output voltage changes automatically after setting value via self.voltage property. - Alternatively, call self.start_voltage_ramp method to initaite voltagte change manually. - self._get_set_property(prop='get_autostart') -> 008: autostart is active - self._get_set_property(prop='get_autostart') -> 000: autostart is inactive - - Returns - ------- - bool - Wheter autostart is active - """ - return self._get_set_property(prop='get_autostart') == '008' - - @autostart.setter - def autostart(self, state): - self._get_set_property(prop='set_autostart', value=8 if state else 0) - - @property - def channel(self): - return self._channel - - @property - def polarity(self): - return 1 if self.module_status[5] == '1' else -1 - - @channel.setter - def channel(self, ch): - if not 1 <= ch <= self.n_channel: - raise ValueError(f"Channel number must be 1 <= channel <= {self.n_channel}") - self._channel = ch - - @property - def UNIT_NUMBER(self): - return self.identifier.split(';')[0] - - @property - def SOFTWARE_REL(self): - return self.identifier.split(';')[1] - - @property - def V_MAX(self): - return self.identifier.split(';')[2] - - @property - def I_MAX(self): - return self.identifier.split(';')[3] - - def __init__(self, intf, conf): - super(IsegHV, self).__init__(intf, conf) - - # Store current channel number; default to channel 1 - self._channel = None - # Store number of channels - self.n_channel = self._init.get('n_channel', 1) - self.channel = self._init.get('channel', 1) - # Voltage which is considered the high voltage - self.high_voltage = self._init.get('high_voltage', None) - # Software-side voltage limit, set via self.v_lim property - self._v_lim = self._init.get('v_lim', None) - - def setup_ps(self): - """Set up the power supply""" - # self.write("") # Synchronize: sometimze needed - - # Important: The manual states that the default answer delay is 3 ms. - # When querying the answer_delay property, it returns sometimes 0 although only values in between 1 and 255 ms are valid. - # Queries take very long which leads to serial timeouts. I suspect the default value on firmware side is in fact 255 ms (not 3 ms). - # Therefore, setting the answer_delay as first thing in the __init__ is sometimes required - self.answer_delay = 1 # ms - - # Add error response for attempting to set voltage too high - self.ERRORS[f'? UMAX={self.voltage_limit}'] = "Set voltage exceeds voltage limit" - def _get_set_property(self, prop, value=None): - if '{channel}' in self.CMDS[prop] and '{value}' in self.CMDS[prop]: + if "{channel}" in self.CMDS[prop] and "{value}" in self.CMDS[prop]: cmd = self.CMDS[prop].format(channel=self.channel, value=value) - elif '{channel}' in self.CMDS[prop]: + elif "{channel}" in self.CMDS[prop]: cmd = self.CMDS[prop].format(channel=self.channel) - elif '{value}' in self.CMDS[prop]: + elif "{value}" in self.CMDS[prop]: cmd = self.CMDS[prop].format(value=value) else: cmd = self.CMDS[prop] @@ -446,22 +456,211 @@ def query(self, msg): # RECV -> actual data // recv the actual data echo = str(self._intf.query(msg)).strip() if echo != msg: - raise RuntimeError(f"Issued command ({msg}) and echoed command ({echo}) differ.") + raise RuntimeError( + f"Issued command ({msg}) and echoed command ({echo}) differ." + ) return self.read() - def start_voltage_ramp(self): - """ - Manually initiate the change of the voltage. - Only needed if self.autostart is False - """ - self._get_set_property(prop='start_voltage_ramp') - - def hv_on(self): + def on(self): try: - _ = float(self.high_voltage) - self.voltage = self.high_voltage + self.voltage = float(self.high_voltage) except TypeError: - raise ValueError("High voltage is not set. Set *high_voltage* attribute to numerical value") + raise ValueError( + "High voltage is not set. Set *high_voltage* attribute to numerical value" + ) + + def off(self): + self.set_voltage(0) + + def get_on(self): + return "0" if self.get_module_status()[4] == "1" else "1" + + def set_voltage_range(self): ... # Not implemented + + def set_current(self): ... # Not implemented + + def source_current(self): ... # Not implemented + + def soruce_volt(self): ... # Not implemented + + @property + def UNIT_NUMBER(self): + return self.identifier().split(";")[0] + + @property + def SOFTWARE_REL(self): + return self.identifier().split(";")[1] + + @property + def V_MAX(self): + return self.identifier().split(";")[2] + + @property + def I_MAX(self): + return self.identifier().split(";")[3] + ##### BEWLOW DEPRECATED ##### + + @property + def hv_on(self): + """ + Use 'on' method instead + """ + self.on() + + @property def hv_off(self): - self.voltage = 0 + """ + Use 'off' method instead + """ + self.off() + + @property + def polarity(self): + """ + Use 'get_polarity' method instead + """ + return self.get_polarity() + + @property + def autostart(self): + """ + Use 'get_autostart' method instead + """ + return self.get_autostart() + + @autostart.setter + def autostart(self, state): + """ + Use 'set_autostart' method instead + """ + self.set_autostart(state) + + @property + def answer_delay(self): + """ + Use 'get_answer_delay' method instead + """ + return self.get_answer_delay() + + @answer_delay.setter + def answer_delay(self, bt): + """ + Use 'set_answer_delay' method instead + """ + self.set_answer_delay(bt) + + @property + def ramp_speed(self): + """ + Use 'get_ramp_speed' method instead + """ + return self.get_ramp_speed() + + @ramp_speed.setter + def ramp_speed(self, rs): + """ + Use 'set_ramp_speed' method instead + """ + self.set_ramp_speed(rs) + + @property + def channel(self): + """ + Use 'get_current_channel' method instead + """ + return self.get_current_channel() + + @channel.setter + def channel(self, ch): + """ + Use 'set_current_channel' method instead + """ + self.set_current_channel(ch) + + @property + def current_trip(self): + """ + Use 'get_current_trip' method instead. + This property will use the mA resolution! + """ + return self.get_current_trip() + + @current_trip.setter + def current_trip(self, ct): + """ + Use 'set_current_trip' method instead + This setter will use the mA resolution! + """ + self.set_current_trip(ct) + + @property + def voltage(self): + """ + Use 'get_voltage' method instead + """ + return self.get_voltage() + + @property + def current(self): + """ + Use 'get_current' method instead + """ + return self.get_current() + + @property + def voltage_target(self): + """ + Use 'get_source_voltage' method instead + """ + return self.get_source_voltage() + + @property + def v_lim(self): + return self.get_voltage_limit() + + @v_lim.setter + def v_lim(self, voltage_limit): + self.set_voltage_limit(voltage_limit) + + @property + def voltage_limit(self): + """ + Use 'get_hardware_voltage_limit' method instead + """ + return self.get_hardware_voltage_limit() + + @property + def identifier(self): + """ + Use 'get_identifier' method instead + """ + return self.get_identifier() + + @property + def status_word(self): + """ + Use 'get_status_word' method instead + """ + return self.get_status_word() + + @property + def module_status(self): + """ + Use 'get_module_status' method instead + """ + return self.get_module_status() + + @property + def module_description(self): + """ + Use 'get_module_description' method instead + """ + return self.get_module_description() + + @property + def status_description(self): + """ + Use 'get_status_description' method instead + """ + return self.get_status_description() From e7ef2ce3fd7d788b58eeb1aa21bfb0797b765428 Mon Sep 17 00:00:00 2001 From: Maximilian Mucha Date: Wed, 13 Nov 2024 16:39:19 +0100 Subject: [PATCH 2/7] PEP8 fixes --- basil/HL/iseg_hv.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/basil/HL/iseg_hv.py b/basil/HL/iseg_hv.py index 7e362d38..0ebbcd4b 100644 --- a/basil/HL/iseg_hv.py +++ b/basil/HL/iseg_hv.py @@ -223,7 +223,7 @@ def get_current_limit(self): int(self._get_set_property(prop="get_current_limit")) / 100.0 * float(self.I_MAX[:-2]) ) - def set_current_trip(self, resolution="mA"): + def get_current_trip(self, resolution="mA"): """ Read current trip/limit, if 0 -> no trip @@ -475,14 +475,18 @@ def off(self): def get_on(self): return "0" if self.get_module_status()[4] == "1" else "1" - def set_voltage_range(self): ... # Not implemented + def set_voltage_range(self): # Not implemented + ... - def set_current(self): ... # Not implemented + def set_current(self): # Not implemented + ... - def source_current(self): ... # Not implemented - - def soruce_volt(self): ... # Not implemented + def source_current(self): # Not implemented + ... + def soruce_volt(self): # Not implemented + ... + @property def UNIT_NUMBER(self): return self.identifier().split(";")[0] @@ -499,7 +503,7 @@ def V_MAX(self): def I_MAX(self): return self.identifier().split(";")[3] - ##### BEWLOW DEPRECATED ##### + #### BEWLOW DEPRECATED #### @property def hv_on(self): From 16548f341e3d1a81bde0ac933821df18fda0be2e Mon Sep 17 00:00:00 2001 From: Maximilian Mucha Date: Wed, 13 Nov 2024 16:43:44 +0100 Subject: [PATCH 3/7] More PEP8 fixes --- basil/HL/iseg_hv.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/basil/HL/iseg_hv.py b/basil/HL/iseg_hv.py index 0ebbcd4b..c02bb93a 100644 --- a/basil/HL/iseg_hv.py +++ b/basil/HL/iseg_hv.py @@ -475,18 +475,18 @@ def off(self): def get_on(self): return "0" if self.get_module_status()[4] == "1" else "1" - def set_voltage_range(self): # Not implemented + def set_voltage_range(self): # Not implemented ... - def set_current(self): # Not implemented + def set_current(self): # Not implemented ... - def source_current(self): # Not implemented + def source_current(self): # Not implemented ... - def soruce_volt(self): # Not implemented + def soruce_volt(self): # Not implemented ... - + @property def UNIT_NUMBER(self): return self.identifier().split(";")[0] @@ -503,8 +503,8 @@ def V_MAX(self): def I_MAX(self): return self.identifier().split(";")[3] - #### BEWLOW DEPRECATED #### - + # BEWLOW DEPRECATED + @property def hv_on(self): """ From 019462d75b91323933bd0bd79c684fd34ab8b000 Mon Sep 17 00:00:00 2001 From: Maximilian Mucha Date: Wed, 13 Nov 2024 16:50:02 +0100 Subject: [PATCH 4/7] last PEP8 fix --- basil/HL/iseg_hv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/basil/HL/iseg_hv.py b/basil/HL/iseg_hv.py index c02bb93a..e482f2b9 100644 --- a/basil/HL/iseg_hv.py +++ b/basil/HL/iseg_hv.py @@ -504,7 +504,7 @@ def I_MAX(self): return self.identifier().split(";")[3] # BEWLOW DEPRECATED - + @property def hv_on(self): """ From f982b2c065eb6621ac66b302ad5e79b9bf81ed5f Mon Sep 17 00:00:00 2001 From: Maximilian Mucha Date: Wed, 13 Nov 2024 18:19:56 +0100 Subject: [PATCH 5/7] Small refactoring fixes --- basil/HL/iseg_hv.py | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/basil/HL/iseg_hv.py b/basil/HL/iseg_hv.py index e482f2b9..9a12937d 100644 --- a/basil/HL/iseg_hv.py +++ b/basil/HL/iseg_hv.py @@ -46,10 +46,12 @@ class IsegHV(HardwareLayer): } FORMATS = { - "get_voltage_meas": lambda val: f"{val[:-3]}e{val[-3:]}", - "get_current_meas": lambda val: f"{val[:-3]}e{val[-3:]}", - "get_voltage_set": lambda val: f"{val[:-3]}e{val[-3:]}", + "get_voltage": lambda val: f"{val[:-3]}e{val[-3:]}", + "get_current": lambda val: f"{val[:-3]}e{val[-3:]}", + "get_source_voltage": lambda val: f"{val[:-3]}e{val[-3:]}", "get_current_trip": lambda val: f"{val[:-3]}e{val[-3:]}", + "get_current_trip_mA": lambda val: f"{val[:-3]}e{val[-3:]}", + "get_current_trip_muA": lambda val: f"{val[:-3]}e{val[-3:]}", } ERRORS = { @@ -81,14 +83,16 @@ def __init__(self, intf, conf): self.n_channel = self._init.get("n_channel", 1) self.channel = self._init.get("channel", 1) - # Voltage which is considered the high voltage + # Voltage which is considered the high voltage when powering on self.high_voltage = self._init.get("high_voltage", None) # Software-side voltage limit, set via self.v_lim property self._voltage_limit = self._init.get("v_lim", None) - if self._voltage_limit is not None: - self.voltage_limit = self._init.get("voltage_limit", None) + if self._voltage_limit is None: + self._voltage_limit = self._init.get("voltage_limit", None) + + self._autostart = self._init.get("autostart", False) def init(self): """Set up the power supply""" @@ -104,6 +108,14 @@ def init(self): self.ERRORS[f"? UMAX={self.get_voltage_limit}"] = ( "Set voltage exceeds voltage limit" ) + + self.set_autostart(self._autostart) + + def set_high_voltage(self, voltage): + self.high_voltage = voltage + + def get_high_voltage(self): + return self.high_voltage def get_current(self): """ @@ -114,7 +126,7 @@ def get_current(self): float Output current in A """ - return float(self._get_set_property(prop="get_current_meas")) + return float(self._get_set_property(prop="get_current")) def get_voltage(self): """ @@ -125,7 +137,7 @@ def get_voltage(self): float Output voltage in V """ - return float(self._get_set_property(prop="get_voltage_meas")) + return float(self._get_set_property(prop="get_voltage")) def set_voltage(self, voltage): # Check hardware voltage limit @@ -463,7 +475,7 @@ def query(self, msg): def on(self): try: - self.voltage = float(self.high_voltage) + self.set_voltage(float(self.high_voltage)) except TypeError: raise ValueError( "High voltage is not set. Set *high_voltage* attribute to numerical value" @@ -489,19 +501,19 @@ def soruce_volt(self): # Not implemented @property def UNIT_NUMBER(self): - return self.identifier().split(";")[0] + return self.get_identifier().split(";")[0] @property def SOFTWARE_REL(self): - return self.identifier().split(";")[1] + return self.get_identifier().split(";")[1] @property def V_MAX(self): - return self.identifier().split(";")[2] + return self.get_identifier().split(";")[2] @property def I_MAX(self): - return self.identifier().split(";")[3] + return self.get_identifier().split(";")[3] # BEWLOW DEPRECATED From 02e1dd9ac8204460329cc8940e1f9ee50fb3835b Mon Sep 17 00:00:00 2001 From: Maximilian Mucha Date: Wed, 13 Nov 2024 18:28:35 +0100 Subject: [PATCH 6/7] updated example --- examples/lab_devices/iseg_shq.py | 44 +++++++++++++++++++++--------- examples/lab_devices/iseg_shq.yaml | 1 + 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/examples/lab_devices/iseg_shq.py b/examples/lab_devices/iseg_shq.py index 5a68b8cf..43f8f807 100644 --- a/examples/lab_devices/iseg_shq.py +++ b/examples/lab_devices/iseg_shq.py @@ -14,35 +14,53 @@ dut.init() # Set PSU channel -dut['SHQ'].channel = 1 +dut['SHQ'].set_current_channel(1) # Set voltage ramp speed in V/s -dut['SHQ'].ramp_speed = 2 +dut['SHQ'].set_ramp_speed(2) # Set autostart to True in order to automatically start voltage change when new voltage is set -dut['SHQ'].autostart = True +dut['SHQ'].set_autostart(True) # Set voltage to +-30 volts; requires hardware polarity change -polarity = dut['SHQ'].polarity +polarity = dut['SHQ'].get_polarity() -dut['SHQ'].high_voltage = 15 * polarity # Set high voltage; hv_on() / hv_off() -dut['SHQ'].v_lim = 20 * polarity # Set software-side voltage limit -dut['SHQ'].voltage = 10 * polarity # Set voltage +dut['SHQ'].set_high_voltage(15 * polarity) # Set high voltage; hv_on() / hv_off() +dut['SHQ'].set_voltage_limit(20 * polarity) # Set software-side voltage limit +dut['SHQ'].set_voltage(10 * polarity) # Set voltage + +dut['SHQ'].set_current_trip(10) # Set current trip in mA +dut['SHQ'].set_current_trip(10000, resolution="muA") # Set current trip in µA # Disable autostart -dut['SHQ'].autostart = False +dut['SHQ'].set_autostart(False) # Read back the voltage that is measured at the output -print(dut['SHQ'].voltage) +print(dut['SHQ'].get_voltage()) + +# Read back the current that is measured at the output +print(dut['SHQ'].get_current()) # Read back the software-side voltage limit -print(dut['SHQ'].v_lim) +print(dut['SHQ'].get_voltage_limit()) + +# Read back the hardware-side voltage-limit +print(dut['SHQ'].get_hardware_voltage_limit()) # Read back the voltage that is set to be the output -print(dut['SHQ'].voltage_target) +print(dut['SHQ'].get_source_voltage()) + +# Read back the current trip (default is mA range) +print(dut['SHQ'].get_current_trip()) + +# Read back the current trip in mA +print(dut['SHQ'].get_current_trip(resolution="mA")) + +# Read back the current trip in µA +print(dut['SHQ'].get_current_trip("muA")) # Print the module description -print(dut['SHQ'].module_description) +print(dut['SHQ'].get_module_description()) # Print the module description -print(dut['SHQ'].identifier) +print(dut['SHQ'].get_identifier()) diff --git a/examples/lab_devices/iseg_shq.yaml b/examples/lab_devices/iseg_shq.yaml index 88070213..72478133 100644 --- a/examples/lab_devices/iseg_shq.yaml +++ b/examples/lab_devices/iseg_shq.yaml @@ -17,3 +17,4 @@ hw_drivers: # v_lim : 100 # Set software-side voltage limit to be 100 V n_channel : 1 # Set number of channels the ISEG HV PS has channel : 1 # Set channel number + autostart : True # Set autostart to True in order to automatically start voltage change when new voltage is set From 59a9cba8e1d9869b5f342a04ef104c0a7c560201 Mon Sep 17 00:00:00 2001 From: Maximilian Mucha Date: Wed, 13 Nov 2024 18:31:30 +0100 Subject: [PATCH 7/7] PEP8 fixes --- basil/HL/iseg_hv.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/basil/HL/iseg_hv.py b/basil/HL/iseg_hv.py index 9a12937d..3b5c0470 100644 --- a/basil/HL/iseg_hv.py +++ b/basil/HL/iseg_hv.py @@ -108,12 +108,12 @@ def init(self): self.ERRORS[f"? UMAX={self.get_voltage_limit}"] = ( "Set voltage exceeds voltage limit" ) - + self.set_autostart(self._autostart) - + def set_high_voltage(self, voltage): self.high_voltage = voltage - + def get_high_voltage(self): return self.high_voltage