class PiLCTriggerGateGenerator(Device): '''PiLCTriggerGateGenerator Provides high-level access to the PiLC Tango interface ''' PiLCFQDN = device_property(dtype=str, default_value='domain/family/member') exposure = attribute( dtype=int, format="%8d", label="Exposure", unit="ms", access=AttrWriteType.READ_WRITE, display_level=DispLevel.OPERATOR, doc="Exposure time in full 10ms steps", memorized=True, ) mode = attribute(dtype=Mode, label="Mode", access=AttrWriteType.READ_WRITE, display_level=DispLevel.OPERATOR, memorized=True, doc=""" 1 - free running 2 - triggered laser 3 - triggered laser & ccd """) def init_device(self): Device.init_device(self) try: self.pilc = DeviceProxy(self.PiLCFQDN) self.info_stream('Connected to PiLC: {:s}'.format(self.PiLCFQDN)) except: self.error_stream('Could not connect to PiLC: {:s}'.format( self.PiLCFQDN)) return self.set_state(DevState.OFF) self.set_state(DevState.ON) self.db = Database() try: attr = self.db.get_device_attribute_property( self.get_name(), ["exposure"]) self._exposure = int(attr["exposure"]["__value"][0]) except Exception: self._exposure = -1 try: attr = self.db.get_device_attribute_property( self.get_name(), ["mode"]) self._mode = int(attr["mode"]["__value"][0]) except Exception: self._mode = 0 def always_executed_hook(self): if self.pilc.ReadFPGA(0x06) > 0: self.set_state(DevState.MOVING) else: self.set_state(DevState.ON) # attribute read/write methods def read_exposure(self): return self._exposure def write_exposure(self, value): self._exposure = int(round(value / 10, 0) * 10) def read_mode(self): return self._mode def write_mode(self, value): self._mode = value # commands @command() def prepare(self): if self._exposure >= 40: shutter_gate_width = (self._exposure - 8) elif self._exposure >= 30: shutter_gate_width = (self._exposure - 7) elif self._exposure >= 20: shutter_gate_width = (self._exposure - 5) else: shutter_gate_width = self._exposure shutter_gate_delay = 0 keithley_gate_width = self._exposure keithley_gate_delay = 8 quantity = 1 self.debug_stream( 'Shutter gate width set to {:d} ms'.format(shutter_gate_width)) self.debug_stream( 'Shutter gate delay set to {:d} ms'.format(shutter_gate_delay)) self.debug_stream( 'Keithley gate width set to {:d} ms'.format(keithley_gate_width)) self.debug_stream( 'Keithley gate delay set to {:d} ms'.format(keithley_gate_delay)) self.debug_stream('Quantity set to {:d}'.format(quantity)) # define gate width in micorseconds self.pilc.WriteFPGA([0x03, int(shutter_gate_width * 1e3)]) # define gate delay in microseconds self.pilc.WriteFPGA([0x07, int(shutter_gate_delay * 1e3)]) # define keithley gate width in micorseconds self.pilc.WriteFPGA([0x09, int(keithley_gate_width * 1e3)]) # define keithley gate delay in microseconds self.pilc.WriteFPGA([0x0B, int(keithley_gate_delay * 1e3)]) # define gate quantity self.pilc.WriteFPGA([0x05, int(quantity)]) @command() def stop(self): self.debug_stream('Stop') self.pilc.WriteFPGA([0x01, 0]) @command() def start(self): self.debug_stream('Start in {:s} mode'.format(Mode(self._mode).name)) self.pilc.WriteFPGA([0x01, self._mode + 1]) @command() def acquire(self): self.debug_stream('Acquire') self.stop() self.prepare() self.start()
class PhytronMCC2Axis(Device): # device properties CtrlDevice = device_property( dtype="str", default_value="domain/family/member" ) Axis = device_property( dtype="int16" ) Address = device_property( dtype="int16" ) Alias = device_property( dtype="str" ) # device attributes hw_limit_minus = attribute( dtype="bool", label="HW limit -", access=AttrWriteType.READ, display_level=DispLevel.OPERATOR, ) hw_limit_plus = attribute( dtype="bool", label="HW limit +", access=AttrWriteType.READ, display_level=DispLevel.OPERATOR, ) sw_limit_minus = attribute( dtype="float", format="%8.3f", label="SW limit -", unit="steps", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) sw_limit_plus = attribute( dtype="float", format="%8.3f", label="SW limit +", unit="steps", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) position = attribute( dtype="float", format="%8.3f", label="position", unit="steps", access=AttrWriteType.READ_WRITE, display_level=DispLevel.OPERATOR, ) alias = attribute( dtype="string", label="alias", access=AttrWriteType.READ, display_level=DispLevel.OPERATOR, ) inverted = attribute( dtype="bool", label="inverted", memorized=True, access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) acceleration = attribute( dtype="int", label="acceleration", unit="Hz", min_value=4000, max_value=500000, access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) velocity = attribute( dtype="int", label="velocity", unit="Hz", min_value=0, max_value=40000, access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) homing_velocity = attribute( dtype="int", label="homing velocity", unit="Hz", min_value=0, max_value=40000, access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) hold_current = attribute( dtype="float", label="hold current", unit="A", min_value=0, max_value=2.5, format="%2.1f", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) run_current = attribute( dtype="float", label="run current", unit="A", min_value=0, max_value=2.5, format="%2.1f", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) initiator_type = attribute( dtype=InitiatorType, label="initiator type", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) steps_per_unit = attribute( dtype="float", format="%10.1f", label="steps per unit", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) step_resolution = attribute( dtype="int", label="step resolution", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, doc="""Step resolution 1 to 256 1 = Full step 2 = Half step 4 = 1/4 step 8 = 1/8 step 10 = 1/10 step 16 = 1/16 step 128 = 1/128 step 256 = 1/256 step""" ) backlash_compensation = attribute( dtype="int", label="backlash compensation", unit="step", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, ) type_of_movement = attribute( dtype=MovementType, label="type of movement", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, doc="""0 = rotational Rotating table, 1 limit switch for mechanical zero (referencing) 1 = linear for XY tables or other linear systems, 2 limit switches: Mechanical zero and limit direction - Limit direction +""" ) movement_unit = attribute( dtype=MovementUnit, label="unit", access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, doc="Allowed unit values are step, mm, inch, degree" ) # private class properties __NACK = chr(0x15) # command failed __LIM_PLUS = 2 __LIM_MINUS = 1 __Axis_Name = '' __HW_Limit_Minus = False __HW_Limit_Plus = False __Inverted = False __Unit = MovementUnit.step __Steps_Per_Unit = 1.0 def init_device(self): super().init_device() self.info_stream("init_device()") if self.Axis == 0: self.__Axis_Name = "X" else: self.__Axis_Name = "Y" self.info_stream("module address: {:d}".format(self.Address)) self.info_stream("module axis: {:d}".format(self.Axis)) self.info_stream("alias: {:s}".format(self.Alias)) try: self.ctrl = DeviceProxy(self.CtrlDevice) self.info_stream("ctrl. device: {:s}".format(self.CtrlDevice)) except DevFailed as df: self.error_stream("failed to create proxy to {:s}".format(df)) sys.exit(255) # check if the CrlDevice ON, if not open the serial port if str(self.ctrl.state()) == "OFF": self.ctrl.open() self.info_stream("controller sucessfully opened") else: self.info_stream("controller was already open") if ("MCC" in self.read_firmware_version()): # read memorized attributes from Database self.db = Database() try: attr = self.db.get_device_attribute_property(self.get_name(), ["inverted"]) if attr["inverted"]["__value"][0] == "true": self.__Inverted = True else: self.__Inverted = False except Exception: self.__Inverted = False self.set_state(DevState.ON) else: self.set_state(DevState.OFF) self.info_stream("HW limit-: {0}".format(self.__HW_Limit_Minus)) self.info_stream("HW limit+: {0}".format(self.__HW_Limit_Plus)) def delete_device(self): self.set_state(DevState.OFF) def always_executed_hook(self): answer = self._send_cmd("SE") if (self.Axis == 0): if self.__Inverted: self.__HW_Limit_Minus = bool(int(answer[2]) & self.__LIM_PLUS) self.__HW_Limit_Plus = bool(int(answer[2]) & self.__LIM_MINUS) else: self.__HW_Limit_Minus = bool(int(answer[2]) & self.__LIM_MINUS) self.__HW_Limit_Plus = bool(int(answer[2]) & self.__LIM_PLUS) moving = not(bool(int(answer[1]) & 1)) else: if self.__Inverted: self.__HW_Limit_Minus = bool(int(answer[6]) & self.__LIM_PLUS) self.__HW_Limit_Plus = bool(int(answer[6]) & self.__LIM_MINUS) else: self.__HW_Limit_Minus = bool(int(answer[6]) & self.__LIM_MINUS) self.__HW_Limit_Plus = bool(int(answer[6]) & self.__LIM_PLUS) moving = not(bool(int(answer[5]) & 1)) self.debug_stream("HW limit-: {0}".format(self.__HW_Limit_Minus)) self.debug_stream("HW limit+: {0}".format(self.__HW_Limit_Plus)) if moving is False: self.set_status("Device in ON") self.set_state(DevState.ON) self.debug_stream("device is: ON") else: self.set_status("Device is MOVING") self.set_state(DevState.MOVING) self.debug_stream("device is: MOVING") # attribute read/write methods def read_hw_limit_minus(self): return self.__HW_Limit_Minus def read_hw_limit_plus(self): return self.__HW_Limit_Plus def read_sw_limit_minus(self): ret = float(self.send_cmd("P24R")) if self.__Inverted: return -1*ret else: return ret def write_sw_limit_minus(self, value): if self.__Inverted: value = -1*value self.send_cmd("P24S{:f}".format(value)) def read_sw_limit_plus(self): ret = float(self.send_cmd("P23R")) if self.__Inverted: return -1*ret else: return ret def write_sw_limit_plus(self, value): if self.__Inverted: value = -1*value self.send_cmd("P23S{:f}".format(value)) def read_position(self): ret = float(self.send_cmd("P20R")) if self.__Inverted: return -1*ret else: return ret def write_position(self, value): if self.__Inverted: value = -1*value answer = self.send_cmd("A{:.10f}".format(value)) if answer != self.__NACK: self.set_state(DevState.MOVING) def read_alias(self): return self.Alias def read_inverted(self): return self.__Inverted def write_inverted(self, value): self.__Inverted = bool(value) def read_acceleration(self): return int(self.send_cmd("P15R")) def write_acceleration(self, value): self.send_cmd("P15S{:d}".format(value)) def read_velocity(self): return int(self.send_cmd("P14R")) def write_velocity(self, value): self.send_cmd("P14S{:d}".format(value)) def read_homing_velocity(self): return int(self.send_cmd("P08R")) def write_homing_velocity(self, value): self.send_cmd("P08S{:d}".format(value)) def read_run_current(self): return float(self.send_cmd("P41R"))/10 def write_run_current(self, value): value = int(value*10) if value not in range(0, 26): return "input not in range 0..25" self.send_cmd("P41S{:d}".format(value)) def read_hold_current(self): return float(self.send_cmd("P40R"))/10 def write_hold_current(self, value): value = int(value*10) if value not in range(0, 26): return "input not in range 0..25" self.send_cmd("P40S{:d}".format(value)) def read_initiator_type(self): return InitiatorType.NOC if bool(int(self.send_cmd("P27R"))) else InitiatorType.NCC def write_initiator_type(self, value): self.send_cmd("P27S{:d}".format(int(value))) def read_steps_per_unit(self): # inverse of spindle pitch (see manual page 50) self.__Steps_Per_Unit = 1/float(self.send_cmd("P03R")) return self.__Steps_Per_Unit def write_steps_per_unit(self, value): # inverse of spindle pitch (see manual page 50) self.send_cmd("P03S{:10.8f}".format(1/value)) # update display unit self.set_display_unit() def read_step_resolution(self): return int(self.send_cmd("P45R")) def write_step_resolution(self, value): if value not in [1, 2, 4, 8, 10, 16, 128, 256]: return "input not in [1, 2, 4, 8, 10, 16, 128, 256]" self.send_cmd("P45S{:d}".format(value)) def read_backlash_compensation(self): ret = int(self.send_cmd("P25R")) if self.__Inverted: return -1*ret else: return ret def write_backlash_compensation(self, value): if self.__Inverted: value = -1*value self.send_cmd("P25S{:d}".format(int(value))) def read_type_of_movement(self): return MovementType.linear if bool(int(self.send_cmd("P01R"))) else MovementType.rotational def write_type_of_movement(self, value): self.send_cmd("P01S{:d}".format(int(value))) def read_movement_unit(self): res = int(self.send_cmd("P02R")) if res == 1: self.__Unit = MovementUnit.step elif res == 2: self.__Unit = MovementUnit.mm elif res == 3: self.__Unit = MovementUnit.inch elif res == 4: self.__Unit = MovementUnit.degree return self.__Unit def write_movement_unit(self, value): self.send_cmd("P02S{:d}".format(int(value+1))) self.read_movement_unit() self.set_display_unit() # internal methods def set_display_unit(self): attributes = [b"position", b"sw_limit_minus", b"sw_limit_plus"] for attr in attributes: ac3 = self.get_attribute_config_3(attr) ac3[0].unit = self.__Unit.name.encode("utf-8") if (1/self.__Steps_Per_Unit % 1) == 0.0: ac3[0].format = b"%8d" else: ac3[0].format = b"%8.3f" self.set_attribute_config_3(ac3) def _send_cmd(self, cmd): # add module address to beginning of command cmd = str(self.Address) + cmd res = self.ctrl.write_read(cmd) if res == self.__NACK: self.set_state(DevState.FAULT) self.warn_stream("command not acknowledged from controller " "-> Fault State") return "" return res # commands @command(dtype_in=str, dtype_out=str, doc_in="enter a command", doc_out="the response") def send_cmd(self, cmd): # add axis name (X, Y) to beginning of command return self._send_cmd(str(self.__Axis_Name) + cmd) @command(dtype_out=str, doc_out="the firmware version") def read_firmware_version(self): version = self._send_cmd("IVR") return version @command(dtype_in=float, doc_in="position") def set_position(self, value): if self.__Inverted: value = -1*value self.send_cmd("P20S{:.4f}".format(value)) @command def jog_plus(self): if self.__Inverted: self.send_cmd("L-") else: self.send_cmd("L+") self.set_state(DevState.MOVING) @command def jog_minus(self): if self.__Inverted: self.send_cmd("L+") else: self.send_cmd("L-") self.set_state(DevState.MOVING) @command def homing_plus(self): if self.__Inverted: self.send_cmd("0-") else: self.send_cmd("0+") self.set_state(DevState.MOVING) @command def homing_minus(self): if self.__Inverted: self.send_cmd("0+") else: self.send_cmd("0-") self.set_state(DevState.MOVING) @command def stop(self): self.send_cmd("S") self.set_state(DevState.ON) @command def abort(self): self.send_cmd("SN") self.set_state(DevState.ON) @command(dtype_in=str) def set_alias(self, name): self.Alias = name self.db.put_device_property(self.get_name(), {"Alias": name}) @command(dtype_out=str) def write_to_eeprom(self): self._send_cmd("SA") self.info_stream("parameters written to EEPROM") return "parameters written to EEPROM" @command(dtype_out=str) def dump_config(self): parameters = range(1, 50) res = "" for par in parameters: cmd = "P{:02d}R".format(par) res = res + "P{:02d}: {:s}\n".format(par, str(self.send_cmd(cmd))) return res