class PowerSupply(Device): host = device_property(str, default_value='localhost') port = device_property(int, default_value=45000) def init_device(self): super().init_device() self.calibration_task = None self.conn = connect(self.host, self.port) @attribute(dtype=float) def voltage(self): return float(write_readline(self.conn, b'VOL?\n')) @command def calibrate(self): if self.calibration_task is not None: raise Exception('Calibration already in place') self.calibration_task = spawn(self.do_calibration) def do_calibration(self): self.set_state(DevState.RUNNING) try: write_readline(self.conn, b'CALIB 1\n') while int(write_readline(self.conn, b'stat?\n')): sleep(0.1) finally: self.set_state(DevState.ON) self.calibration_task = None
class Merlin(Device): host = device_property(dtype=str, mandatory=False) port = device_property(dtype=int, mandatory=False) def init_device(self): super().init_device() self._control = MerlinControl(host=self.host, port=self.port) @command def connect(self): self._control.connect() @command def close(self): self._control.close() @command(dtype_in=str, dtype_out=str) def get(self, param): with self._control: return self._control.get(param) @command(dtype_in=(str, )) def set(self, param_value): param, value = param_value with self._control: return self._control.set(param, value) @command(dtype_in=str) def cmd(self, cmd): with self._control: return self._control.cmd(cmd) @command(dtype_out=str) def hello(self): return "world"
class MythenSLS(Device): host = device_property(dtype=str) ctrl_port = device_property(dtype=int, default_value=DEFAULT_CTRL_PORT) stop_port = device_property(dtype=int, default_value=DEFAULT_STOP_PORT) def init_device(self): super().init_device() self.ctrl = get_control() @property def mythen(self): return self.ctrl.hwInterface().detector def dev_state(self): status = self.mythen.run_status if status in (RunStatus.WAITING, RunStatus.TRANSMITTING, RunStatus.RUNNING): return DevState.RUNNING elif status == RunStatus.ERROR: return DevState.FAULT return DevState.ON def dev_status(self): return repr(self.mythen) @attribute def exposure_time_left(self): return self.mythen.exposure_time_left @attribute def nb_cycles_left(self): return self.mythen.nb_cycles_left @attribute def nb_frames_left(self): return self.mythen.nb_frames_left @attribute def progress(self): return self.mythen.progress @attribute def measurement_time(self): return self.mythen.measurement_time @attribute(dtype=int) def energy_threshold(self): return self.mythen.energy_threshold @energy_threshold.setter def energy_threshold(self, energy_threshold): self.mythen.energy_threshold = energy_threshold
class FuelCell(Device): url = device_property(dtype=str, doc='fuell cell hostname') name = device_property(dtype=str, default_value=None) def init_device(self): Device.init_device(self) try: if self.name: config = get_config() self.fuelcell = config.get(self.name) else: self.fuelcell = _FuelCell('Fuel Cell', dict(tcp=dict(url=self.url))) self.set_state(DevState.ON) self.set_status('Ready!') except Exception as e: self.set_state(DevState.FAULT) self.set_status('Error:\n' + str(e)) # ---------- # Commands # ---------- @command(dtype_in=[int], doc_in=Ptc.timescan.__doc__) def timescan(self, par_list): self.fuelcell.ptc.timescan(*par_list) @command(dtype_in=[str], doc_in=Ptc.cv.__doc__) def cv(self, par_list): if len(par_list) < 7: raise RuntimeError('not enough parameters') channel = par_list[0] start = float(par_list[1]) stop = float(par_list[2]) margin1 = float(par_list[3]) margin2 = float(par_list[4]) speed = float(par_list[5]) sweeps = int(par_list[6]) self.fuelcell.ptc.cv(channel, start, stop, margin1, margin2, speed, sweeps) @command(doc_in=Ptc.stop.__doc__) def stop(self): self.fuelcell.ptc.stop() def write_vsense(self, value): self.fuelcell.ptc.set_vsense_feedback(value) def write_current(self, value): self.fuelcell.ptc.set_current_feedback(value)
class BaseJulabo(Device): url = device_property(dtype=str) baudrate = device_property(dtype=int, default_value=9600) bytesize = device_property(dtype=int, default_value=8) parity = device_property(dtype=str, default_value='N') Julabo = None async def init_device(self): await super().init_device() kwargs = dict(concurrency="asyncio") if self.url.startswith("serial") or self.url.startswith("rfc2217"): kwargs = dict(baudrate=self.baudrate, bytesize=self.bytesize, parity=self.parity) connection = connection_for_url(self.url, **kwargs) self.julabo = self.Julabo(connection) async def dev_state(self): status_code = int((await self.julabo.status())[:2]) if status_code in {0, 2}: return DevState.STANDBY elif status_code in {1, 3}: return DevState.RUNNING elif status_code < 0: return DevState.ALARM return DevState.FAULT async def dev_status(self): return await self.julabo.status() @attribute(dtype=str, label="Identification") async def identification(self): """Device identification (model and version)""" return await self.julabo.identification() @attribute(dtype=bool, label="Is started?") async def is_started(self): """Is device started or not""" return await self.julabo.is_started() @command async def start(self): """Start the device""" await self.julabo.start() @command async def stop(self): """Stop the device""" await self.julabo.stop()
class MandatoryPropertyServer(Device): Hostname = device_property( dtype='str', mandatory=True, doc='The controller host address') Port = device_property( dtype='int', default_value=3456, doc='The controller port number') def init_device(self): Device.init_device(self) print('Port: ', self.Port) print('Host: ', self.Hostname) self.set_state(DevState.ON)
class TestDevice(Device): """.""" _start_time = time.time() version = device_property(dtype=str, default_value=__version__) def init_device(self): """.""" Device.init_device(self) self.set_state(DevState.STANDBY) def always_executed_hook(self): """.""" def delete_device(self): """.""" @attribute(dtype=str) def identifier(self): """Return the device name.""" return type(self).__name__ @attribute(dtype=float) def up_time(self): """.""" return time.time() - self._start_time # pylint: disable=no-self-use @command(dtype_in=str, dtype_out=str) def cmd_str_arg(self, value: str) -> str: """.""" time.sleep(0.01) return value
class Omicron_laser(Device): url = device_property(dtype=str) def init_device(self): super().init_device() self.connection = serial.serial_for_url(self.url) self.omicron_laser = omicron_laser.core.Omicron_laser(self.connection) ################################################################################ @attribute(dtype=float, unit="bar", label="Pressure set point") def pressure_setpoint(self): # example processing the result setpoint = self.omicron_laser.get_pressure_setpoint() return setpoint / 1000 @pressure_setpoint.setter def pressure_setpoint(self, value): # example returning the coroutine back to tango return self.omicron_laser.get_pressure_setpoint(value * 1000) @command def turn_on(self): # example returning the coroutine back to who calling function return self.omicron_laser.turn_on()
class SKATelState(SKABaseDevice): """ A generic base device for Telescope State for SKA. """ # PROTECTED REGION ID(SKATelState.class_variable) ENABLED START # # PROTECTED REGION END # // SKATelState.class_variable # ----------------- # Device Properties # ----------------- TelStateConfigFile = device_property( dtype='str', ) # ---------- # Attributes # ---------- # --------------- # General methods # --------------- def always_executed_hook(self): # PROTECTED REGION ID(SKATelState.always_executed_hook) ENABLED START # pass # PROTECTED REGION END # // SKATelState.always_executed_hook def delete_device(self): # PROTECTED REGION ID(SKATelState.delete_device) ENABLED START # pass
class TestDevice1(Device): green_mode = server_green_mode prop1 = device_property(dtype=str) @command(dtype_out=str) def get_prop1(self): return self.prop1
class TestDevice(Device): green_mode = server_green_mode prop = device_property(dtype=dtype) @command(dtype_out=patched_dtype) def get_prop(self): return default if self.prop is None else self.prop
class TestDevice(Device): green_mode = server_green_mode prop = device_property(dtype=dtype, mandatory=True) @command(dtype_out=patched_dtype) def get_prop(self): return self.prop
class TestDevice2(Device): green_mode = server_green_mode prop2 = device_property(dtype=int) @command(dtype_out=int) def get_prop2(self): return self.prop2
class TestDevice(Device): green_mode = server_green_mode prop = device_property(dtype=dtype, default_value=default) @command(dtype_out=dtype) def get_prop(self): return self.prop
class PowerSupply(Device): host = device_property(str, default_value='localhost') port = device_property(int, default_value=45000) def init_device(self): super().init_device() self.conn = connect(self.host, self.port) @attribute(dtype=float) def voltage(self): return float(write_readline(self.conn, b'VOL?\n')) @command def calibrate(self): write_readline(self.conn, b'CALIB 1\n') while int(write_readline(self.conn, b'stat?\n')): sleep(0.1)
class OneAxis(Device): axis = device_property(dtype=str, default_value='1') def init_device(self): self.axis = self.get_name().split('/')[2] self.pi_device = PiDevice try: self.pi_device.FRF(self.axis) except: print(self.pi_device.qERR()) print("done") #TODO query axises and reference every @attribute(dtype=float) def position(self): return self.pi_device.qPOS().get(self.axis) @attribute(dtype=bool) def servo(self): return self.pi_device.qSVO().get(self.axis) @attribute(dtype=bool) def referenced(self): return self.pi_device.qFRF().get(self.axis) def state(self): return self.pi_device.qSVO(self.axis) def status(self): return "IsConnected: " + self.pi_device.IsConnected() @command(dtype_in=None) def enable_servo(self): self.pi_device.SVO(self.axis, 1) @command(dtype_in=None) def disable_servo(self): self.pi_device.SVO(self.axis, 0) @command(dtype_in=float, dtype_out=None) def move(self, pos): if not self.pi_device.qSVO(self.axis): raise Exception("not in servo mode, enable first") self.pi_device.MOV(self.axis, pos) # Command axis "A" to position pos @command(dtype_in=None) def stop(self): self.pi_device.HLT(self.axis) @command(dtype_in=None) def stop_all(self): self.pi_device.STP()
class Master(Device): WorkerFQDNs = device_property(dtype="DevVarStringArray") @command(dtype_in="DevLong") def Turn_Worker_On(self, worker_id): worker_fqdn = self.WorkerFQDNs[worker_id - 1] worker_device = tango.DeviceProxy(worker_fqdn) worker_device.is_on = True @command(dtype_in="DevLong") def Turn_Worker_Off(self, worker_id): worker_fqdn = self.WorkerFQDNs[worker_id - 1] worker_device = tango.DeviceProxy(worker_fqdn) worker_device.is_on = False
class A(Device): green_mode = server_green_mode prop1 = device_property(dtype=str, default_value="hello1") prop2 = device_property(dtype=str, default_value="hello2") @command(dtype_out=str) def get_prop1(self): return self.prop1 @command(dtype_out=str) def get_prop2(self): return self.prop2 @attribute(access=AttrWriteType.READ_WRITE) def attr(self): return self.attr_value @attr.write def attr(self, value): self.attr_value = value def dev_status(self): return ")`'-.,_"
class Hamamatsu(Device): camera_id = device_property(dtype=int, default_value=0) def init_device(self): super().init_device() self.ctrl = get_control() @property def mythen(self): return self.ctrl.hwInterface().detector def dev_state(self): status = self.mythen.status return DevState.RUNNING if status == "RUNNING" else DevState.ON
class SKATelState(with_metaclass(DeviceMeta, SKABaseDevice)): """ A generic base device for Telescope State for SKA. """ # PROTECTED REGION ID(SKATelState.class_variable) ENABLED START # # PROTECTED REGION END # // SKATelState.class_variable # ----------------- # Device Properties # ----------------- TelStateConfigFile = device_property( dtype='str', ) # ---------- # Attributes # ---------- # --------------- # General methods # --------------- def init_device(self): """init_device Init device method of SKATelStateDevice """ SKABaseDevice.init_device(self) self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # PROTECTED REGION ID(SKATelState.init_device) ENABLED START # # PROTECTED REGION END # // SKATelState.init_device def always_executed_hook(self): # PROTECTED REGION ID(SKATelState.always_executed_hook) ENABLED START # pass # PROTECTED REGION END # // SKATelState.always_executed_hook def delete_device(self): # PROTECTED REGION ID(SKATelState.delete_device) ENABLED START # pass
class Multimeter(Device): name = device_property(dtype=str, doc='keithley bliss object name') def init_device(self): Device.init_device(self) try: config = get_config() self.device = config.get(self.name) switch_state(self, DevState.ON, "Ready!") except Exception as e: msg = "Exception initializing device: {0}".format(e) self.error_stream(msg) switch_state(self, DevState.FAULT, msg) def delete_device(self): if self.device: self.device.abort() @attribute(dtype=bool) def auto_range(self): return self.device.get_auto_range() @auto_range.setter def auto_range(self, auto_range): self.device.set_auto_range(auto_range) @attribute(dtype=float) def range(self): return self.get_range() @range.setter def range(self, range): self.set_range(range) @attribute(dtype=float) def nplc(self): return self.device.get_nplc() @nplc.setter def nplc(self, nplc): self.device.set_nplc(nplc)
class B(A): prop2 = device_property(dtype=str, default_value="goodbye2") @attribute def attr2(self): return 3.14 def dev_status(self): return 3 * A.dev_status(self) if server_green_mode == GreenMode.Asyncio: code = textwrap.dedent("""\ @asyncio.coroutine def dev_status(self): coro = super(type(self), self).dev_status() result = {YIELD_FROM}(coro) {RETURN}(3*result) """).format(**globals()) exec(code)
class P04_beamline(Device): DYN_ATTRS = [ dict(name='photonenergy', label='photon energy', dtype=tango.DevFloat, access=READ_WRITE, unit='eV', format='%6.2f', min_value=240, max_value=2000), dict(name='exitslit', label="exit slit", dtype=tango.DevFloat, access=READ_WRITE, unit="um", format="%4.0f"), dict(name='helicity', label='helicity', dtype=tango.DevLong, access=READ_WRITE, min_value=-1, max_value=1), dict(name='mono', label="monochromator", dtype=tango.DevFloat, access=READ, unit="eV", format="%6.2f"), dict(name='undugap', label='undulator gap', dtype=tango.DevFloat, access=READ, unit='mm'), # dict(name='undufactor', label='undulator scale factor', # access=READ, format='%3.2f', dtype=tango.DevFloat), dict(name='undushift', label='undulator shift', dtype=tango.DevFloat, access=READ, unit='mm'), dict(name='ringcurrent', label='ring current', dtype=tango.DevFloat, access=READ, unit='mA'), # dict(name='keithley1', label='beamline keithley', dtype=tango.DevFloat, # access=READ), # dict(name='keithley2', label='user keithley', dtype=tango.DevFloat, # access=READ), dict(name='slt2hleft', label='slit hor left', dtype=tango.DevFloat, access=READ), dict(name='slt2hright', label='slit hor right', dtype=tango.DevFloat, access=READ), dict(name='slt2vgap', label='slit ver gap', dtype=tango.DevFloat, access=READ), dict(name='slt2voffset', label='slit ver offset', dtype=tango.DevFloat, access=READ), # dict(name='exsu2bpm', label='exsu2bpm', dtype=tango.DevFloat, # access=READ), # dict(name='exsu2baffle', label='exsu2baffle', dtype=tango.DevFloat, # access=READ), # dict(name='pressure', label='experiment pressure', access=READ, # dtype=tango.DevFloat, unit='mbar', format='%.2E'), dict(name='screen', label='beamline screen', dtype=tango.DevLong, access=READ_WRITE, min_value=0, max_value=2, enum_labels=['closed', 'mesh', 'open']) ] ready_to_move = attribute(name='ready_to_move', label='in position', access=READ, dtype=tango.DevBoolean, polling_period=1000, fread="is_movable") host = device_property(dtype=str, mandatory=True, update_db=True) port = device_property(dtype=int, default_value=3002) def init_device(self): Device.init_device(self) self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.connect((self.host, self.port)) self.s.setblocking(True) self.lock = Lock() self.set_state(DevState.ON) energy = self.read_attr('photonenergy')[0] self._setpoint_E = [energy, energy] self._setpoint_helicity = self.read_attr('helicity')[0] def initialize_dynamic_attributes(self): # TODO: setup polling and event filter for d in self.DYN_ATTRS: new_attr = attribute(fget=self.read_general, fset=self.write_general, **d) self.add_attribute(new_attr) @command(dtype_in=str) def query(self, msg): '''Send a query and wait for its reply.''' if self.lock.acquire(timeout=0.5): if not msg.endswith(' eoc'): msg += ' eoc' # print('sent:', msg, file=self.log_debug) self.s.sendall(msg.encode()) ans = self.s.recv(1024).decode() # print('received:', ans, file=self.log_debug) assert ans.endswith('eoa') self.lock.release() return ans[:-4] else: print(f"can't send '{msg}': socket is locked", file=self.log_error) return 'busy' def read_general(self, attr): key = attr.get_name() # print('reading', key, file=self.log_debug) val, time, quality = self.read_attr(key) attr.set_value(val) # def write_general(self, attr): # key = attr.get_name() # val = attr.get_write_value() # send_attrs = ['photonenergy', 'exitslit', 'helicity', 'screen'] # cmd = 'send' if key in send_attrs else 'set' # ntries = 0 # while ntries < 10: # ntries += 1 # if self.is_movable(): # ans = self.query(f'{cmd} {key} {val}') # print(f'setting {key}: {val} ({ntries}/10)', file=self.log_debug) # if ans == 'started': # self.set_state(DevState.MOVING) # print(f'[key] moving to {val}', file=self.log_debug) # return # time.sleep(1) # print(f'could not send {key} to {val}', file=self.log_error) def write_general(self, attr): key = attr.get_name() val = attr.get_write_value() send_attrs = ['photonenergy', 'exitslit', 'helicity', 'screen'] cmd = 'send' if key in send_attrs else 'set' if key == 'photonenergy': self._setpoint_E[0] = val if key == 'helicity': self._setpoint_helicity = val ans = self.query(f'{cmd} {key} {val}') print(f'setting {key}: {val}', file=self.log_debug) if ans == 'started': self._setpoint_E[1] = val self.set_state(DevState.MOVING) print(f'[key] moving to {val}', file=self.log_debug) return print(f'could not send {key} to {val}', file=self.log_error) def is_movable(self): '''Check whether undulator and monochromator are in position.''' in_pos = self.query('check photonenergy') in_pos = True if in_pos == '1' else False if (self._setpoint_E[0] != self._setpoint_E[1]) and in_pos: ans_set = self.query(f'send photonenergy {self._setpoint_E[0]}') if ans_set == 'started': self._setpoint_E[1] = self._setpoint_E[0] helicity = self.read_attr('helicity')[0] state = (helicity == self._setpoint_helicity) and in_pos self.set_state(DevState.ON if state else DevState.MOVING) return in_pos @command(dtype_in=str, dtype_out=str) def cmd_async(self, msg, test): '''Send a command without waiting for it to finish. The socket will still be blocked! ''' t = Thread(target=self.query, args=(msg, )) t.daemon = True t.start() @command def closeconnection(self): ans = self.query('closeconnection') if 'bye!' in ans: self.s.close() self.set_state(DevState.OFF) def read_attr(self, attr): '''Queries the position of given attribute name. Returns ------- val : float tstamp : time stamp quality : AttrQuality instance (ATTR_VALID, ATTR_CHANGING, ...) ''' ans = self.query(f'read {attr}') if 'Current value' in ans: val = float(ans.split(':')[1]) return val, time(), AttrQuality.ATTR_VALID else: self.error_stream('Socket busy or unexpected/incomplete answer') return None, time(), AttrQuality.ATTR_INVALID
class SKACapability(with_metaclass(DeviceMeta, SKAObsDevice)): """ A Subarray handling device. It exposes the instances of configured capabilities. """ # PROTECTED REGION ID(SKACapability.class_variable) ENABLED START # # PROTECTED REGION END # // SKACapability.class_variable # ----------------- # Device Properties # ----------------- CapType = device_property(dtype='str', ) CapID = device_property(dtype='str', ) subID = device_property(dtype='str', ) # ---------- # Attributes # ---------- activationTime = attribute( dtype='double', unit="s", standard_unit="s", display_unit="s", doc="Time of activation in seconds since Unix epoch.", ) configuredInstances = attribute( dtype='uint16', doc= "Number of instances of this Capability Type currently in use on this subarray.", ) usedComponents = attribute( dtype=('str', ), max_dim_x=100, doc= "A list of components with no. of instances in use on this Capability.", ) # --------------- # General methods # --------------- def init_device(self): SKAObsDevice.init_device(self) self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # PROTECTED REGION ID(SKACapability.init_device) ENABLED START # self._activation_time = 0.0 self._configured_instances = 0 self._used_components = [""] # PROTECTED REGION END # // SKACapability.init_device def always_executed_hook(self): # PROTECTED REGION ID(SKACapability.always_executed_hook) ENABLED START # pass # PROTECTED REGION END # // SKACapability.always_executed_hook def delete_device(self): # PROTECTED REGION ID(SKACapability.delete_device) ENABLED START # pass # PROTECTED REGION END # // SKACapability.delete_device # ------------------ # Attributes methods # ------------------ def read_activationTime(self): # PROTECTED REGION ID(SKACapability.activationTime_read) ENABLED START # """ Reads time of activation since Unix epoch. :return: Activation time in seconds """ return self._activation_time # PROTECTED REGION END # // SKACapability.activationTime_read def read_configuredInstances(self): # PROTECTED REGION ID(SKACapability.configuredInstances_read) ENABLED START # """ Reads the number of instances of a capability in the subarray :return: The number of configured instances of a capability in a subarray """ return self._configured_instances # PROTECTED REGION END # // SKACapability.configuredInstances_read def read_usedComponents(self): # PROTECTED REGION ID(SKACapability.usedComponents_read) ENABLED START # """ Reads the list of components with no. of instances in use on this Capability :return: The number of components currently in use. """ return self._used_components # PROTECTED REGION END # // SKACapability.usedComponents_read # -------- # Commands # -------- @command( dtype_in='uint16', doc_in="The number of instances to configure for this Capability.", ) @DebugIt() def ConfigureInstances(self, argin): # PROTECTED REGION ID(SKACapability.ConfigureInstances) ENABLED START # """ This function indicates how many number of instances of the current capacity should to be configured. :param argin: Number of instances to configure :return: None. """ self._configured_instances = argin
class SKAMaster(with_metaclass(DeviceMeta, SKABaseDevice)): """ A master test """ # PROTECTED REGION ID(SKAMaster.class_variable) ENABLED START # # PROTECTED REGION END # // SKAMaster.class_variable # ----------------- # Device Properties # ----------------- # List of maximum number of instances per capability type provided by this Element; # CORRELATOR=512, PSS-BEAMS=4, PST-BEAMS=6, VLBI-BEAMS=4 or for DSH it can be: # BAND-1=1, BAND-2=1, BAND3=0, BAND-4=0, BAND-5=0 (if only bands 1&2 is installed) MaxCapabilities = device_property(dtype=('str', ), ) # ---------- # Attributes # ---------- elementLoggerAddress = attribute( dtype='str', doc="FQDN of Element Logger", ) elementAlarmAddress = attribute( dtype='str', doc="FQDN of Element Alarm Handlers", ) elementTelStateAddress = attribute( dtype='str', doc="FQDN of Element TelState device", ) elementDatabaseAddress = attribute( dtype='str', doc="FQDN of Element Database device", ) maxCapabilities = attribute( dtype=('str', ), max_dim_x=20, doc= "Maximum number of instances of each capability type, e.g. 'CORRELATOR:512', 'PSS-BEAMS:4'.", ) availableCapabilities = attribute( dtype=('str', ), max_dim_x=20, doc="A list of available number of instances of each capability type, " "e.g. 'CORRELATOR:512', 'PSS-BEAMS:4'.", ) # --------------- # General methods # --------------- def init_device(self): SKABaseDevice.init_device(self) # PROTECTED REGION ID(SKAMaster.init_device) ENABLED START # self._build_state = '{}, {}, {}'.format(release.name, release.version, release.description) self._version_id = release.version # Initialize attribute values. self._element_logger_address = "" self._element_alarm_address = "" self._element_tel_state_address = "" self._element_database_address = "" self._element_alarm_device = "" self._element_tel_state_device = "" self._element_database_device = "" self._max_capabilities = {} if self.MaxCapabilities: for max_capability in self.MaxCapabilities: capability_type, max_capability_instances = max_capability.split( ":") self._max_capabilities[capability_type] = int( max_capability_instances) self._available_capabilities = self._max_capabilities.copy() # PROTECTED REGION END # // SKAMaster.init_device def always_executed_hook(self): # PROTECTED REGION ID(SKAMaster.always_executed_hook) ENABLED START # pass # PROTECTED REGION END # // SKAMaster.always_executed_hook def delete_device(self): # PROTECTED REGION ID(SKAMaster.delete_device) ENABLED START # pass # PROTECTED REGION END # // SKAMaster.delete_device # ------------------ # Attributes methods # ------------------ def read_elementLoggerAddress(self): # PROTECTED REGION ID(SKAMaster.elementLoggerAddress_read) ENABLED START # """Reads FQDN of Element Logger device""" return self._element_logger_address # PROTECTED REGION END # // SKAMaster.elementLoggerAddress_read def read_elementAlarmAddress(self): # PROTECTED REGION ID(SKAMaster.elementAlarmAddress_read) ENABLED START # """Reads FQDN of Element Alarm device""" return self._element_alarm_address # PROTECTED REGION END # // SKAMaster.elementAlarmAddress_read def read_elementTelStateAddress(self): # PROTECTED REGION ID(SKAMaster.elementTelStateAddress_read) ENABLED START # """Reads FQDN of Element TelState device""" return self._element_tel_state_address # PROTECTED REGION END # // SKAMaster.elementTelStateAddress_read def read_elementDatabaseAddress(self): # PROTECTED REGION ID(SKAMaster.elementDatabaseAddress_read) ENABLED START # """Reads FQDN of Element Database device""" return self._element_database_address # PROTECTED REGION END # // SKAMaster.elementDatabaseAddress_read def read_maxCapabilities(self): # PROTECTED REGION ID(SKAMaster.maxCapabilities_read) ENABLED START # """Reads maximum number of instances of each capability type""" return convert_dict_to_list(self._max_capabilities) # PROTECTED REGION END # // SKAMaster.maxCapabilities_read def read_availableCapabilities(self): # PROTECTED REGION ID(SKAMaster.availableCapabilities_read) ENABLED START # """Reads list of available number of instances of each capability type""" return convert_dict_to_list(self._available_capabilities) # PROTECTED REGION END # // SKAMaster.availableCapabilities_read # -------- # Commands # -------- @command( dtype_in='DevVarLongStringArray', doc_in="[nrInstances][Capability types]", dtype_out='bool', ) @DebugIt() def isCapabilityAchievable(self, argin): # PROTECTED REGION ID(SKAMaster.isCapabilityAchievable) ENABLED START # """ Checks of provided capabilities can be achieved by the resource(s). :param argin: DevVarLongStringArray. An array consisting pair of [nrInstances]: DevLong. Number of instances of the capability. [Capability types]: DevString. Type of capability. :return: DevBoolean True if capability can be achieved. False if cannot. """ command_name = 'isCapabilityAchievable' capabilities_instances, capability_types = argin validate_input_sizes(command_name, argin) validate_capability_types(command_name, capability_types, list(self._max_capabilities.keys())) for capability_type, capability_instances in zip( capability_types, capabilities_instances): if not self._available_capabilities[ capability_type] >= capability_instances: return False return True
class FspPssSubarray(CspSubElementObsDevice): """ FspPssSubarray TANGO device class for the FspPssSubarray prototype """ # PROTECTED REGION ID(FspPssSubarray.class_variable) ENABLED START # # PROTECTED REGION END # // FspPssSubarray.class_variable # ----------------- # Device Properties # ----------------- SubID = device_property(dtype='uint16') FspID = device_property(dtype='uint16') CbfControllerAddress = device_property( dtype='str', doc="FQDN of CBF Controller", default_value="mid_csp_cbf/controller/main") # TODO: CbfSubarrayAddress prop not being used CbfSubarrayAddress = device_property(dtype='str', doc="FQDN of CBF Subarray") VCC = device_property(dtype=('str', )) # ---------- # Attributes # ---------- receptors = attribute( dtype=('uint16', ), access=AttrWriteType.READ, max_dim_x=197, label="Receptors", doc="List of receptors assigned to subarray", ) searchBeams = attribute( dtype=('str', ), access=AttrWriteType.READ, max_dim_x=192, label="SearchBeams", doc="List of searchBeams assigned to fspsubarray", ) searchWindowID = attribute( dtype='uint16', access=AttrWriteType.READ, max_dim_x=2, label="ID for 300MHz Search Window", doc= "Identifier of the Search Window to be used as input for beamforming on this FSP.", ) searchBeamID = attribute( dtype=('uint16', ), access=AttrWriteType.READ, max_dim_x=192, label="ID for 300MHz Search Window", doc= "Identifier of the Search Window to be used as input for beamforming on this FSP.", ) outputEnable = attribute( dtype='bool', access=AttrWriteType.READ, label="Enable Output", doc="Enable/disable transmission of output products.", ) # --------------- # General methods # --------------- def init_command_objects(self): """ Sets up the command objects """ super().init_command_objects() device_args = (self, self.state_model, self.logger) self.register_command_object("ConfigureScan", self.ConfigureScanCommand(*device_args)) self.register_command_object("GoToIdle", self.GoToIdleCommand(*device_args)) class InitCommand(CspSubElementObsDevice.InitCommand): """ A class for the Vcc's init_device() "command". """ def do(self): """ Stateless hook for device initialisation. :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ self.logger.debug("Entering InitCommand()") device = self.target # Make a private copy of the device properties: device._subarray_id = device.SubID device._fsp_id = device.FspID # initialize attribute values device._receptors = [] device._search_beams = [] device._search_window_id = 0 device._search_beam_id = [] device._output_enable = 0 device._scan_id = 0 device._config_id = "" # device proxy for easy reference to CBF Controller device._proxy_cbf_controller = tango.DeviceProxy( device.CbfControllerAddress) device._controller_max_capabilities = dict( pair.split(":") for pair in device._proxy_cbf_controller.get_property( "MaxCapabilities")["MaxCapabilities"]) device._count_vcc = int(device._controller_max_capabilities["VCC"]) device._fqdn_vcc = list(device.VCC)[:device._count_vcc] device._proxies_vcc = [*map(tango.DeviceProxy, device._fqdn_vcc)] message = "FspPssSubarry Init command completed OK" self.logger.info(message) return (ResultCode.OK, message) # PROTECTED REGION END # // FspPssSubarray.init_device def always_executed_hook(self): # PROTECTED REGION ID(FspPssSubarray.always_executed_hook) ENABLED START # """hook before any commands""" pass # PROTECTED REGION END # // FspPssSubarray.always_executed_hook def delete_device(self): # PROTECTED REGION ID(FspPssSubarray.delete_device) ENABLED START # """Set Idle, remove all receptors, turn device OFF""" pass # PROTECTED REGION END # // FspPssSubarray.delete_device # ------------------ # Attributes methods # ------------------ def read_receptors(self): # PROTECTED REGION ID(FspPssSubarray.receptors_read) ENABLED START # """return receptros attribute.(array of int)""" return self._receptors # PROTECTED REGION END # // FspPssSubarray.receptors_read def read_searchBeams(self): # PROTECTED REGION ID(FspPssSubarray.searchBeams_read) ENABLED START # """Return searchBeams attribute (JSON)""" return self._search_beams # PROTECTED REGION END # // FspPssSubarray.searchBeams_read def read_searchBeamID(self): # PROTECTED REGION ID(FspPssSubarray.read_searchBeamID ENABLED START # """REturn list of SearchBeam IDs(array of int). (From searchBeams JSON)""" return self._search_beam_id # PROTECTED REGION END # // FspPssSubarray.read_searchBeamID def read_searchWindowID(self): # PROTECTED REGION ID(CbfSubarrayPssConfig.read_searchWindowID) ENABLED START # """Return searchWindowID attribtue(array of int)""" return self._search_window_id # PROTECTED REGION END # // CbfSubarrayPssConfig.read_searchWindowID def read_outputEnable(self): # PROTECTED REGION ID(CbfSubarrayPssConfig.read_outputEnable) ENABLED START # """Enable/Disable transmission of the output products""" return self._output_enable # PROTECTED REGION END # // CbfSubarrayPssConfig.read_outputEnable # -------- # Commands # -------- def _add_receptors(self, receptorIDs): """add specified receptors to the FSP subarray. Input is array of int.""" self.logger.debug("_AddReceptors") errs = [] # list of error messages receptor_to_vcc = dict( [*map(int, pair.split(":"))] for pair in self._proxy_cbf_controller.receptorToVcc) for receptorID in receptorIDs: try: vccID = receptor_to_vcc[receptorID] subarrayID = self._proxies_vcc[vccID - 1].subarrayMembership # only add receptor if it belongs to the CBF subarray if subarrayID != self._subarray_id: errs.append( "Receptor {} does not belong to subarray {}.".format( str(receptorID), str(self._subarray_id))) else: if receptorID not in self._receptors: self._receptors.append(receptorID) else: # TODO: this is not true if more receptors can be # specified for the same search beam log_msg = "Receptor {} already assigned to current FSP subarray.".format( str(receptorID)) self.logger.warn(log_msg) except KeyError: # invalid receptor ID errs.append("Invalid receptor ID: {}".format(receptorID)) if errs: msg = "\n".join(errs) self.logger.error(msg) tango.Except.throw_exception("Command failed", msg, "AddReceptors execution", tango.ErrSeverity.ERR) # PROTECTED REGION END # // FspPssSubarray.AddReceptors def _remove_receptors(self, argin): """Remove Receptors. Input is array of int""" self.logger.debug("_remove_receptors") for receptorID in argin: if receptorID in self._receptors: self._receptors.remove(receptorID) else: log_msg = "Receptor {} not assigned to FSP subarray. "\ "Skipping.".format(str(receptorID)) self.logger.warn(log_msg) def _remove_all_receptors(self): self._remove_receptors(self._receptors[:]) # -------- # Commands # -------- class ConfigureScanCommand(CspSubElementObsDevice.ConfigureScanCommand): """ A class for the FspPssSubarray's ConfigureScan() command. """ """Input a serilized JSON object. """ def do(self, argin): """ Stateless hook for ConfigureScan() command functionality. :param argin: The configuration as JSON formatted string :type argin: str :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) :raises: ``CommandError`` if the configuration data validation fails. """ device = self.target argin = json.loads(argin) # Configure receptors. self.logger.debug("_receptors = {}".format(device._receptors)) device._fsp_id = argin["fsp_id"] device._search_window_id = int(argin["search_window_id"]) self.logger.debug("_search_window_id = {}".format( device._search_window_id)) for searchBeam in argin["search_beam"]: if len(searchBeam["receptor_ids"]) != 1: # TODO - to add support for multiple receptors msg = "Currently only 1 receptor per searchBeam is supported" self.logger.error(msg) return (ResultCode.FAILED, msg) device._add_receptors(map(int, searchBeam["receptor_ids"])) self.logger.debug("device._receptors = {}".format( device._receptors)) device._search_beams.append(json.dumps(searchBeam)) device._search_beam_id.append(int( searchBeam["search_beam_id"])) # TODO: _output_enable is not currently set # TODO - possibly move validation of params to # validate_input() # (result_code, msg) = self.validate_input(argin) # TODO result_code = ResultCode.OK # TODO - temp - remove msg = "Configure command completed OK" # TODO temp, remove if result_code == ResultCode.OK: # store the configuration on command success device._last_scan_configuration = argin msg = "Configure command completed OK" return (result_code, msg) def validate_input(self, argin): """ Validate the configuration parameters against allowed values, as needed. :param argin: The JSON formatted string with configuration for the device. :type argin: 'DevString' :return: A tuple containing a return code and a string message. :rtype: (ResultCode, str) """ device = self.target return (ResultCode.OK, "ConfigureScan arguments validation successfull") @command( dtype_in='DevString', doc_in="JSON formatted string with the scan configuration.", dtype_out='DevVarLongStringArray', doc_out= "A tuple containing a return code and a string message indicating status. " "The message is for information purpose only.", ) @DebugIt() def ConfigureScan(self, argin): # PROTECTED REGION ID(Vcc.ConfigureScan) ENABLED START # """ Configure the observing device parameters for the current scan. :param argin: JSON formatted string with the scan configuration. :type argin: 'DevString' :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ command = self.get_command_object("ConfigureScan") (return_code, message) = command(argin) return [[return_code], [message]] class GoToIdleCommand(CspSubElementObsDevice.GoToIdleCommand): """ A class for the FspPssSubarray's GoToIdle command. """ def do(self): """ Stateless hook for GoToIdle() command functionality. :return: A tuple containing a return code and a string message indicating status. The message is for information purpose only. :rtype: (ResultCode, str) """ self.logger.debug("Entering GoToIdleCommand()") device = self.target # initialize attribute values device._search_beams = [] device._search_window_id = 0 device._search_beam_id = [] device._output_enable = 0 device._scan_id = 0 device._config_id = "" device._remove_all_receptors() if device.state_model.obs_state == ObsState.IDLE: return (ResultCode.OK, "GoToIdle command completed OK. Device already IDLE") return (ResultCode.OK, "GoToIdle command completed OK")
class DCP3000(Device): green_mode = GreenMode.Asyncio url = device_property(dtype=str) baudrate = device_property(dtype=int, default_value=9600) bytesize = device_property(dtype=int, default_value=8) parity = device_property(dtype=str, default_value="N") dcp = None def url_to_connection_args(self): url = self.url res = urllib.parse.urlparse(url) kwargs = dict(concurrency="async") if res.scheme in {"serial", "rfc2217"}: kwargs.update( dict(baudrate=self.baudrate, bytesize=self.bytesize, parity=self.parity)) return url, kwargs async def init_device(self): await super().init_device() url, kwargs = self.url_to_connection_args() conn = connection_for_url(url, **kwargs) self.dcp = dcp3000.DCP3000(conn) self.last_values = {} async def read_attr_hardware(self, indexes): multi_attr = self.get_device_attr() names = [ multi_attr.get_attr_by_ind(index).get_name().lower() for index in indexes ] funcs = (ATTR_MAP[name] for name in names) values = [await func(self.dcp) for func in funcs] self.last_values = dict(zip(names, values)) async def dev_state(self): state = DevState.ON try: errors = await self.dcp.errors() if errors: state = DevState.ALARM except: state = DevState.FAULT return state async def dev_status(self): self.__status = "Ready!" try: errors = await self.dcp.errors() if errors: self.__status = "Hardware error(s):\n" + "\n".join(errors) except Exception as error: self.__status = "Communication error:\n{!r}".format(error) return self.__status @attribute(unit="mbar") def pressure(self): return self.last_values["pressure"] @attribute(unit="mbar", dtype=[float], max_dim_x=8) def transducer_pressures(self): return self.last_values["transducer_pressures"] @attribute(unit="mbar") def transducer_pressure_1(self): return self.last_values["transducer_pressure_1"] @attribute(unit="mbar") def transducer_pressure_2(self): return self.last_values["transducer_pressure_2"] @attribute(unit="mbar") def transducer_pressure_3(self): return self.last_values["transducer_pressure_3"] @attribute(unit="mbar") def transducer_pressure_4(self): return self.last_values["transducer_pressure_4"] @attribute(dtype=[str]) def errors(self): return self.last_values["errors"] @attribute(dtype=str) def version(self): return self.last_values["version"] @attribute() def on_setpoint_1(self): return self.last_values["on_setpoint_1"] @on_setpoint_1.setter def on_setpoint_1(self, value): return self.dcp.on_setpoint(1, value) @attribute() def on_setpoint_2(self): return self.last_values["on_setpoint_2"] @on_setpoint_2.setter def on_setpoint_2(self, value): return self.dcp.on_setpoint(2, value) @attribute() def on_setpoint_3(self): return self.last_values["on_setpoint_3"] @on_setpoint_3.setter def on_setpoint_3(self, value): return self.dcp.on_setpoint(3, value) @attribute() def on_setpoint_4(self): return self.last_values["on_setpoint_4"] @on_setpoint_4.setter def on_setpoint_4(self, value): return self.dcp.on_setpoint(4, value) @attribute() def off_setpoint_1(self): return self.last_values["off_setpoint_1"] @off_setpoint_1.setter def off_setpoint_1(self, value): return self.dcp.off_setpoint(1, value) @attribute() def off_setpoint_2(self): return self.last_values["off_setpoint_2"] @off_setpoint_2.setter def off_setpoint_2(self, value): return self.dcp.off_setpoint(2, value) @attribute() def off_setpoint_3(self): return self.last_values["off_setpoint_3"] @off_setpoint_3.setter def off_setpoint_3(self, value): return self.dcp.off_setpoint(3, value) @attribute() def off_setpoint_4(self): return self.last_values["off_setpoint_4"] @off_setpoint_4.setter def off_setpoint_4(self, value): return self.dcp.off_setpoint(4, value) @command def switch_on(self): return self.dcp.switch_on() @command def switch_off(self): return self.dcp.switch_off() @command def open_venting_valve(self): return self.dcp.open_venting_value() @command def close_venting_valve(self): return self.dcp.close_venting_valve() @command def vent(self): return self.dcp.vent()
class AgilisAGAP(Device): Address = device_property( dtype='int16', ) Port = device_property( dtype='str', ) # some Constants __AXIS_X = 'U' __AXIS_Y = 'V' # Errors from page 64 of the manual __ERROR_NEG_END_OF_RUN = 1 __ERROR_POS_END_OF_RUN = 2 __ERROR_OUT_OF_RANGE = ('G', 'C') # States from page 65 of the manual __STATE_READY = ('32', '33', '34', '35', '36') __STATE_MOVING = ('28', '29') position_x = attribute( label='Position X', dtype='float', access=AttrWriteType.READ_WRITE, format="%4.3f", doc = 'absolute position X' ) position_y = attribute( label='Position Y', dtype='float', access=AttrWriteType.READ_WRITE, format="%4.3f", doc = 'absolute position Y' ) def init_device(self): Device.init_device(self) self.set_state(DevState.INIT) try: self.info_stream("Connecting to AgilisAGAP on port: {:s} ...".format(self.Port)) self.serial = serial.Serial( port = self.Port, baudrate = 921600, bytesize = 8, stopbits = 1, parity = 'N', xonxoff = True, timeout = 0.05) if self.serial.isOpen(): self.serial.close() self.serial.open() self.info_stream("Success!") self.info_stream('Connection established:\n{:s}\n{:s}'.format(self.query('ID?'), self.query('VE?'))) except: self.error_stream("Cannot connect!") self.set_state(DevState.OFF) self.set_state(DevState.ON) def delete_device(self): if self.serial.isOpen(): self.serial.close() self.info_stream('Connection closed for port {:s}'.format(self.Port)) def always_executed_hook(self): res = self.query('TS?') if (res != ''): err = int(res[:4],16) state = res[4:] if (state in self.__STATE_MOVING): self.set_status('Device is MOVING') self.set_state(DevState.MOVING) elif (state in self.__STATE_READY): self.set_status('Device is ON') self.set_state(DevState.ON) else: self.set_status('Device is UNKOWN') self.set_state(DevState.UNKNOWN) def read_position_x(self): return float(self.query('TP' + self.__AXIS_X + '?')) def write_position_x(self, value): self.query('PA' + self.__AXIS_X + str(value)) err = self.get_cmd_error_string() if err in self.__ERROR_OUT_OF_RANGE: Except.throw_exception('x position out of range', 'x position out of range', 'write_position_x') else: self.set_state(DevState.MOVING) def read_position_y(self): return float(self.query('TP' + self.__AXIS_Y + '?')) def write_position_y(self, value): self.query('PA' + self.__AXIS_Y + str(value)) err = self.get_cmd_error_string() if err in self.__ERROR_OUT_OF_RANGE: Except.throw_exception('y position out of range', 'y position out of range', 'write_position_y') else: self.set_state(DevState.MOVING) @command def Stop(self): self.query('ST') @command() def Reset(self): self.query('RS') def query(self, cmd): prefix = str(self.Address) + cmd[:-1] self.send_cmd(cmd) answer = self.serial.readline().decode('utf-8') if answer.startswith(prefix): answer = answer[len(prefix):].strip() else: answer = '' return answer def send_cmd(self, cmd): cmd = str(self.Address) + cmd + '\r\n' self.serial.flushInput() self.serial.flushOutput() self.serial.write(cmd.encode('utf-8')) self.serial.flush() def get_cmd_error_string(self): error = self.query('TE?') return error.strip()
class TemplateDeviceServer(Device): ''' This docstring should describe your Tango Class and optionally what it depends on (drivers etc). ''' # ------ Attributes ------ # humidity = attribute(label='Humidity', dtype=float, access=AttrWriteType.READ, doc='Example for an attribute that can only be read.') # optionally use fget/fset to point to read and write functions. # Default is "read_temperature"/"write_temperature". # Added some optional attribute properties. temperature = attribute(label='Temperature', fget='get_temperature', dtype=float, access=AttrWriteType.READ_WRITE, display_level=DispLevel.EXPERT, min_value=-273.15, min_alarm=-100, max_alarm=100, min_warning=-50, max_warning=50, unit='C', format="8.4f", doc='Attribute that can be read/written.') # ------ Device Properties ------ # # device_properties will be set once per family-member and usually - # contain serial numbers or a certain port-ID that needs to be set once - # and will not change while the server is running. port = device_property(dtype=int, default_value=10000) # ------ default functions that are inherited from parent "Device" ------ # def init_device(self): Device.init_device(self) self.info_stream('Connection established') # prints this line while - # in logging mode "info" or lower self.set_state(DevState.ON) # here you could initiate first contact to the hardware (driver) self.__temp = 0 # declaring values for the attributes if needed self.__humid = 0 def delete_device(self): self.set_state(DevState.OFF) self.error_stream('A device was deleted!') # prints this line while - # in logging mode "error" or lower. # define what is executed when Tango checks for the state. # Here you could inquire the state of the hardware and not just - # (as it is in default) of the TDS. # Default returns state but also sets state from ON to ALARM if - # some attribute alarm limits are surpassed. def dev_state(self): # possible pseudo code: # if hardware-state and TDS-state is ON: # return DevState.ON # else: # return DevState.FAULT return DevState def always_executed_hook(self): # a method that is executed continuously and by default does nothing. # if you want smth done polled/continuously, put it in this method. # check connection to hardware or whether status is acceptable etc. pass # ------ Read/Write functions ------ # def read_humidity(self): # this is default to read humidity return self.__humid # returns the value of the "humidity" attr. def get_temperature(self): # this was set by fget in attribute declaration return self.__temp def write_temperature(self, value): # possibly execute some function here to talk to the hardware - # (e.g. set temperature with a thermostat) self.__temp = value # update the declared server value of the attr. # ------ Internal Methods ------ # # method that works with multiple input parameters only "inside" this code def internal_method(self, param1, param2): # do something with param1, param2 pass # ------ COMMANDS ------ # @DebugIt() # let the execution of this command be logged in debugging mode @command() # make method executable through the client - # (as opposed to just callable inside this code) def external_method(self, param): # this kind of method only allows one input parameter pass # more examples of externally executable methods @command() def turn_off(self): self.set_state(DevState.OFF) @command() def turn_on(self): self.set_state(DevState.ON)
class EC301DS(Device): """ An SRS EC301 device Device States Description: # # DevState.ON : The device is in operation # DevState.INIT : Initialisation of the communication with the device and initial configuration # DevState.FAULT : The Device has a problem and cannot handle further requests. # DevState.MOVING : The Device is engaged in an acquisition. """ ### Properties ### Host = device_property(dtype=str, default_value="b-nanomax-ec301-0", doc="hostname") Port = device_property(dtype=int, default_value=1680) ### Attributes ### @attribute(label='Voltage', dtype=float, doc='Single voltage measurement', unit='V', format='1.3f') def voltage(self): return self.ec301.voltage @attribute(label='Current', dtype=float, doc='Single current measurement', unit='A', format='.3e') def current(self): return self.ec301.current @attribute(label='ID', dtype=str, doc='Device ID string') def id(self): return self.ec301.id @attribute(label='Error', dtype=str, doc='Last error message') def error(self): return self.ec301.error @attribute(label='Running', dtype=bool, doc='acquiring/scanning') def running(self): return self.ec301.running @attribute(label='Mode', dtype=str, doc='Control mode') def mode(self): return self.ec301.mode @mode.write def mode(self, mod): self.ec301.mode = mod @attribute( label='Enabled', dtype=bool, doc= 'Cell enabled? Querying gives false if the front panel switch is out.') def enabled(self): return self.ec301.enabled @enabled.write def enabled(self, stat): self.ec301.enabled = stat @attribute(label='I Range', dtype=int, doc='Current range as log(range/A)') def Irange(self): return self.ec301.Irange @Irange.write def Irange(self, rng): self.ec301.Irange = rng @attribute(label='E Range', dtype=int, doc='Potential range (2, 5, or 15)', unit='V') def Erange(self): return self.ec301.Erange @Erange.write def Erange(self, rng): self.ec301.Erange = rng @attribute(label='Autorange', dtype=bool, doc='Autorange on/off') def autorange(self): return self.ec301.autorange @autorange.write def autorange(self, val): self.ec301.autorange = val @attribute(label='Averaging', dtype=int, doc='Sample averaging') def averaging(self): return self.ec301.averaging # should perhaps be an enum @averaging.write def averaging(self, avg): self.ec301.averaging = avg @attribute(label='Bandwidth', dtype=int, doc='Control loop bandwidth as log(bw/Hz)') def bandwidth(self): return self.ec301.bandwidth @bandwidth.write def bandwidth(self, bw): self.ec301.bandwidth = bw @attribute(label='I lowpass 10kHz', dtype=bool, doc='Enables 10 kHz low-pass filter in front of I ADC') def Ilowpass(self): return self.ec301.Ilowpass @Ilowpass.write def Ilowpass(self, val): self.ec301.Ilowpass = val @attribute(label='E lowpass 10kHz', dtype=bool, doc='Enables 10 kHz low-pass filter in front of E ADC') def Elowpass(self): return self.ec301.Elowpass @Elowpass.write def Elowpass(self, val): self.ec301.Elowpass = val @attribute(label='Compliance limit', dtype=float, doc='Limit on the compliance voltage', unit='V') def compliance_limit(self): return self.ec301.compliance_limit @compliance_limit.write def compliance_limit(self, val): self.ec301.compliance_limit = val ### Commands ### @command(dtype_in=float) def setPotential(self, pot): self.ec301.setPotential(pot) @command(dtype_in=float) def setCurrent(self, cur): self.ec301.setCurrent(cur) @command(dtype_in=str) def acquire(self, arg_list): arg_list = arg_list.strip() time, trigger = [eval(s) for s in arg_list.split()] self.ec301.acquire(time, trigger) @command(dtype_in=str) def potentialStep(self, arg_list): arg_list = arg_list.strip() t0, t1, E0, E1, trigger, full_bandwidth, return_to_E0 = [ eval(s) for s in arg_list.split() ] self.ec301.potentialStep(t0, t1, E0, E1, trigger, full_bandwidth, return_to_E0) @command(dtype_in=str) def potentialCycle(self, arg_list): arg_list = arg_list.strip() t0, E0, E1, E2, v, cycles, trigger = [ eval(s) for s in arg_list.split() ] self.ec301.potentialCycle(t0, E0, E1, E2, v, cycles, trigger) @command() def stop(self): self.ec301.stop() @command(dtype_out=(float, )) def readout_t(self): t, E, I, aux, raw = self.ec301.readout() return t @command(dtype_out=(float, )) def readout_E(self): t, E, I, aux, raw = self.ec301.readout() return E @command(dtype_out=(float, )) def readout_I(self): t, E, I, aux, raw = self.ec301.readout() return I @command(dtype_out=(float, )) def readout_sync_adc(self): t, E, I, aux, raw = self.ec301.readout() return aux @command(dtype_out=(float, )) def readout_raw(self): t, E, I, aux, raw = self.ec301.readout() return raw ### Other stuff ### # Device methods def init_device(self): """Instantiate device object, do initial instrument configuration.""" self.set_state(DevState.INIT) try: self.get_device_properties() self.ec301 = ec301lib.EC301(host=self.Host, port=self.Port) except Exception as e: self.set_state(DevState.FAULT) self.set_status('Device failed at init: %s' % e.message) self.set_state(DevState.ON) # This sets the state before every command def always_executed_hook(self): if self.ec301.running: self.set_state(DevState.MOVING) else: self.set_state(DevState.ON)