.. How-To try How to PyTango ============== :audience:`developers`, :lang:`python` My list of short recipes for common tasks ... please check official_ documentation first! Before anything else -------------------- .. code-block:: python :linenos: import PyTango Installation notes  ------------------- The new PyTango, is now available to download from the Tango download_ page or PiPy_ If you have already installed PyTango with *pip* you can simply update your PyTango version by doing: .. code-block:: console :linenos: $ pip -U PyTango .. code-block:: console :linenos: $ sudo apt-get update python-pytango The documentation is available at official_ readthedocs website. If you encounter problems installing or running this release, please report them back to the tango mailing list. You can check out this version: .. code-block:: console :linenos: $ svn co https://tango-cs.svn.sourceforge.net/svnroot/tango-cs/PyTango/tags/Release_7_1_3 PyTango-7.1.3` You can check out the latest version: .. code-block:: console :linenos: $ svn co https://tango-cs.svn.sourceforge.net/svnroot/tango-cs/PyTango/trunk PyTango-latest` Nice PyTango dancing Using the DeviceProxy object ---------------------------- **Getting the polling buffer values** Only for polled attributes we can get the last N read values. the polling buffer depth is managed by the admin device. .. code-block:: python :linenos: dp = PyTango.DeviceProxy('some/tango/device') dp.attribute_history('cpustatus',10) Get/Set polled attributes ************************* .. code-block:: python :linenos: def get_polled_attributes(dev_name): dp = PyTango.DeviceProxy(dev_name) attrs = dp.get_attribute_list() periods = [(a,dp.get_attribute_poll_period(a)) for a in attrs] return dict((a,p) for a,p in periods if p) [plc4.poll_attribute(a,5000) for k,v in periods if v] Modify the polling of attributes ******************************** .. code-block:: python :linenos: import re,PyTango period = 10000 devs = PyTango.Database().get_device_exported('some/tango/devices*') for dev in devs: dp = PyTango.DeviceProxy(dev) attrs = sorted([a for a in dp.get_attribute_list() if re.match('(Output|Temperature)_[0-9]$',a)]) [dp.poll_attribute(a,period) for a in attrs] print('\n'.join(dp.polling_status())) Events ****** Creating an event callback .. code-block:: python :linenos: # The callback must be a callable or an object with a push_event(self,event) method Configuring an event .. code-block:: python :linenos: #From the client side #subscribe_event(attr_name, event_type, cb_or_queuesize, filters=[], stateless=False, extract_as=PyTango._PyTango.ExtractAs.Numpy) event_id = PyTango.DeviceProxy.subscribe_event(attributeName,PyTango.EventType.CHANGE_EVENT,callback_function,[],True) #From inside the device server self.set_change_event('State',True,True) Device Server Internal Objects ------------------------------ Forcing in which host the device is exported ******************************************** This environment variable must be set before launching the device: .. code-block:: console :linenos: $ export OMNIORB_USEHOSTNAME=10.0.0.10 Creating a Device Server from ipython ************************************* Having defined your device in MyDS.py: .. code-block:: python :linenos: from MyDS import * py = PyTango.PyUtil(['MyDS.py','InstanceName']) py.add_TgClass(MyDSClass,MyDS,'MyDS') U = PyTango.Util.instance() U.server_init() U.server_run() Get the device server admin *************************** NOT TESTED .. code-block:: python :linenos: U = PyTango.Util.instance() U.get_dserver_device() Modify internal polling *********************** .. note:: It doesn't work at *init_device()*; must be done later on in a *hook* method. .. code-block:: python :linenos: U = PyTango.Util.instance() admin = U.get_dserver_device() dir(admin) [ StartPolling StopPolling AddObjPolling RemObjPolling UpdObjPollingPeriod DevPollStatus PolledDevice ] polled_attrs = {} for st in admin.DevPollStatus(name): lines = st.split('\n') try: polled_attrs[lines[0].split()[-1]]=lines[1].split()[-1] except: pass type_ = 'command' or 'attribute' for aname in args: if aname in polled_attrs: admin.UpdObjPollingPeriod([[200],[name,type_,aname]]) else: admin.AddObjPolling([[3000],[name,type_,aname]]) Get all polling attributes ************************** The polling of the attributes is recorded in the **property_device** table of the tango database in the format of a list like *[ATTR1,PERIOD1,ATTR2,PERIOD2,...]* The list of polled attributes can be accessed using this method of admin device: .. code-block:: python :linenos: dp = PyTango.DeviceProxy('dserver/myServerClass/id22') polled_attrs = [a.split('\n')[0].split(' ')[-1] for a in dp.DevPollStatus('domain/family/member-01')] Get the device class object from the device itself ************************************************** .. code-block:: python :linenos: self.get_device_class() Get the devices inside a Device Server ************************************** .. code-block:: python :linenos: def get_devs_in_server(self,MyClass=None): """ Method for getting a dictionary with all the devices running in this server """ MyClass = MyClass or type(self) or DynamicDS if not hasattr(MyClass,'_devs_in_server'): MyClass._devs_in_server = {} #This dict will keep an access to the class objects instantiated in this Tango server if not MyClass._devs_in_server: U = PyTango.Util.instance() for klass in U.get_class_list(): for dev in U.get_device_list_by_class(klass.get_name()): if isinstance(dev,DynamicDS): MyClass._devs_in_server[dev.get_name()]=dev return MyClass._devs_in_server Identify each attribute inside read_attr_hardware() *************************************************** .. code-block:: python :linenos: def read_attr_hardware(self,data): self.debug("In DynDS::read_attr_hardware()") try: attrs = self.get_device_attr() for d in data: a_name = attrs.get_attr_by_ind(d).get_name() if a_name in self.dyn_attrs: self.lock.acquire() #This lock will be released at the end of read_dyn_attr self.myClass.DynDev=self #VITAL: It tells the admin class which device attributes are going to be read self.lock_acquired += 1 self.debug('DynamicDS::read_attr_hardware(): lock acquired %d times'%self.lock_acquired) except Exception,e: self.last_state_exception = 'Exception in read_attr_hardware: %s'%str(e) self.error('Exception in read_attr_hardware: %s'%str(e)) Device server logging (using Tango logs) **************************************** .. code-block:: console :linenos: $ Device_4Impl. $ $ debug_stream ( str ) $ info_stream ( str ) $ warning_stream ( str ) $ error_stream ( str ) $ fatal_stream ( str ) Or use fandango.Logger object instead Adding dynamic attributes to a device ************************************* .. code-block:: python :linenos: self.add_attribute( PyTango.Attr( #or PyTango.SpectrumAttr new_attr_name,PyTango.DevArg.DevState,PyTango.AttrWriteType.READ, #or READ_WRITE #max_size or dyntype.dimx #If Spectrum ), self.read_new_attribute, #(attr) None, #self.write_new_attribute #(attr) self.is_new_attribute_allowed, #(request_type) ) Using Database Object --------------------- .. code-block:: python :linenos: import PyTango db = PyTango.Database() Register a new device server **************************** .. code-block:: python :linenos: dev = 'SR%02d/VC/ALL'%sector klass = 'PyStateComposer' server = klass+'/'+dev.replace('/','_') di = PyTango.DbDevInfo() di.name,di._class,di.server = device,klass,server db.add_device(di) Remove "empty" servers from database ************************************ .. code-block:: python :linenos: tango = PyTango.Database() [tango.delete_server(s) for s in tango.get_server_list() if all(d.lower().startswith('dserver') for d in tango.get_device_class_list(s)) ] Force unexport of a failing server ********************************** You can check using db object if a device is still exported after killed .. code-block:: console :linenos: $ bool(db.import_device('dserver/HdbArchiver/11').exported) $ True You can unexport this device or server with the following call: .. code-block:: python :linenos: db.unexport_server('HdbArchiver/11') It would normally allow you to restart the server again. Get all servers of a given class ******************************** .. code-block:: python :linenos: class_name = 'Modbus' list_of_names = ['/'.join((class_name,name)) for name in db.get_instance_name_list(class_name)] Differences between DB methods: .. code-block:: python :linenos: get_instance_name_list(exec_name): return names of **instances** get_server_list(): returns list of all **executable/instance** get_server_name_list(): return names of all **executables** Get all devices of a server or a given class ******************************************** The command is: .. code-block:: python :linenos: db.get_device_class_list(server_name): return ['device/name/family','device_class']*num_of_devs_in_server The list returned includes the admin server (*dserver/exec_name/instance*) that must be pruned from the result: .. code-block:: python :linenos: list_of_devs = [dev for dev in db.get_device_class_list(server_name) if '/' in dev and not dev.startswith('dserver')] Get all devices of a given class from the database ************************************************** .. code-block:: python :linenos: import operator list_of_devs = reduce(operator.add,(list(dev for dev in db.get_device_class_list(n) \ if '/' in dev and not dev.startswith('dserver')) for n in \ ('/'.join((class_name,instance)) for instance in db.get_instance_name_list(class_name)) \ )) Get property values for a list of devices ***************************************** .. code-block:: python :linenos: db.get_device_property_list(device_name,'*') : returns list of available properties db.get_device_property(device_name,[property_name]) : return {property_name : value} .. code-block:: python :linenos: prop_names = db.get_device_property_list(device_name) ['property1','property2'] dev_props = db.get_device_property(device_name,prop_names) {'property1':'first_value' , 'property2':'second_value' } Get the history (last ten values) of a property *********************************************** .. code-block:: python :linenos: [ph.get_value().value_string for ph in tango.get_device_property_history('some/alarms/device','AlarmsList')] [['MyAlarm:a/gauge/controller/Pressure>1e-05', 'TempAlarm:a/nice/device/Temperature_Max > 130'], Get the server for a given device ********************************* .. code-block:: console :linenos: >>> print db.get_server_list('Databaseds/*') ['DataBaseds/2'] >>> print db.get_device_name('DataBaseds/2','DataBase') ['sys/database/2'] >>> db_dev=PyTango.DeviceProxy('sys/database/2') >>> print db_dev.command_inout('DbImportDevice','et/wintest/01') ([0, 2052], ['et/wintest/01', 'IOR:0100000017000xxxxxx', '4', 'WinTest/manu', 'PCTAUREL.esrf.fr', 'WinTest']) Get the Info of a not running device (exported, host, server) ************************************************************* .. code-block:: python :linenos: def get_device_info(dev): vals = PyTango.DeviceProxy('sys/database/2').DbGetDeviceInfo(dev) di = dict((k,v) for k,v in zip(('name','ior','level','server','host','started','stopped'),vals[1])) di['exported'],di['PID'] = vals[0] return di Set property values for a list of devices ***************************************** **Attention** , Tango property values are always inserted as lists! {property_name : **[** property_value **]**} .. code-block:: python :linenos: prop_name,prop_value = 'Prop1','Value1' [db.put_device_property(dev,{prop_name:[prop_value]}) for dev in list_of_devs] Get Starter Level configuration for a list of servers ***************************************************** .. code-block:: python :linenos: [(si.name,si.mode,si.level) for si in [db.get_server_info(s) for s in list_of_servers]] Set Memorized Value for an Attribute ************************************ .. code-block:: python :linenos: db.get_device_attribute_property('tcoutinho/serial/01/Baudrate',['__value']) db.put_device_attribute_property('tcoutinho/serial/01/Baudrate',{'__value':VALUE}) Useful constants and enums -------------------------- .. code-block:: console :linenos: In [31]:PyTango.ArgType.values Out[31]: {0: PyTango._PyTango.ArgType.DevVoid, 1: PyTango._PyTango.ArgType.DevBoolean, 2: PyTango._PyTango.ArgType.DevShort, 3: PyTango._PyTango.ArgType.DevLong, 4: PyTango._PyTango.ArgType.DevFloat, 5: PyTango._PyTango.ArgType.DevDouble, 6: PyTango._PyTango.ArgType.DevUShort, 7: PyTango._PyTango.ArgType.DevULong, 8: PyTango._PyTango.ArgType.DevString, 9: PyTango._PyTango.ArgType.DevVarCharArray, 10: PyTango._PyTango.ArgType.DevVarShortArray, 11: PyTango._PyTango.ArgType.DevVarLongArray, 12: PyTango._PyTango.ArgType.DevVarFloatArray, 13: PyTango._PyTango.ArgType.DevVarDoubleArray, 14: PyTango._PyTango.ArgType.DevVarUShortArray, 15: PyTango._PyTango.ArgType.DevVarULongArray, 16: PyTango._PyTango.ArgType.DevVarStringArray, 17: PyTango._PyTango.ArgType.DevVarLongStringArray, 18: PyTango._PyTango.ArgType.DevVarDoubleStringArray, 19: PyTango._PyTango.ArgType.DevState, 20: PyTango._PyTango.ArgType.ConstDevString, 21: PyTango._PyTango.ArgType.DevVarBooleanArray, 22: PyTango._PyTango.ArgType.DevUChar, 23: PyTango._PyTango.ArgType.DevLong64, 24: PyTango._PyTango.ArgType.DevULong64, 25: PyTango._PyTango.ArgType.DevVarLong64Array, 26: PyTango._PyTango.ArgType.DevVarULong64Array} In [30]:PyTango.AttrWriteType.values Out[30]: {0: PyTango._PyTango.AttrWriteType.READ, 1: PyTango._PyTango.AttrWriteType.READ_WITH_WRITE, 2: PyTango._PyTango.AttrWriteType.WRITE, 3: PyTango._PyTango.AttrWriteType.READ_WRITE} In [29]:PyTango.AttrWriteType.values[3] is PyTango.READ_WRITE Out[29]:True Using Tango Groups ------------------ This example uses PyTangoGroup to read the status of all devices in a Device Server .. code-block:: python :linenos: import PyTango server_name = 'VacuumController/AssemblyArea' group = PyTango.Group(server_name) devs = [d for d in PyTango.Database().get_device_class_list(server_name) if '/' in d and 'dserver' not in d] for d in devs: group.add(d) answers = group.command_inout('Status',[]) for reply in answers: print 'Device %s Status is:' % reply.dev_name() print reply.get_data() About Exceptions ---------------- Be aware that I'm not sure about all of this: .. code-block:: python :linenos: try: #reason,desc(ription),origin PyTango.Except.throw_exception("TimeWAITBetweenRetries", "Last communication failed at %s, waiting %s millis"%(time.ctime(self.last_failed),self.ErrorTimeWait), inspect.currentframe().f_code.co_name) except PyTango.DevFailed,e: if e.args[0]['reason']!='API_AsynReplyNotArrived': PyTango.Except.re_throw_exception(e,"DevFailed Exception",str(e),inspect.currentframe().f_code.co_name) Passing Arguments to Device command_inout ----------------------------------------- When type of Arguments is *special* like **DevVarLongStringArray** the introduction of arguments is something like: .. code-block:: python :linenos: api.manager.command_inout('UpdateSnapComment',[[40],['provant,provant...']]) Using asynchronous commands --------------------------- .. code-block:: python :linenos: cid = self.modbus.command_inout_asynch(command,arr_argin) while True: self.debug('Waiting for asynchronous answer ...') threading.Event().wait(0.1) #time.sleep(0.1) try: result = self.modbus.command_inout_reply(cid) self.debug('Received: %s' % result) break except PyTango.DevFailed,e: self.debug('Received DevFailed: %s' %e) if e.args[0]['reason'] != 'API_AsynReplyNotArrived': raise Exception,'Weird exception received!: %s' % e Setting Attribute Config ------------------------ .. code-block:: python :linenos: for server in astor.values(): for dev in server.get_device_list(): dp = server.get_proxy(dev) attrs = dp.get_attribute_list() if dev.rsplit('/')[-1].lower() not in [a.lower() for a in attrs]: continue conf = dp.get_attribute_config(dev.rsplit('/')[-1]) conf.format = "%1.1e" conf.unit = "mbar" conf.label = "%s-Pressure"%dev print 'setting config for %s/%s' % (dev,conf.name) dp.set_attribute_config(conf) Porting device servers to PyTango --------------------------------- The changes to easily port PyTango_ devices are: - C++ : Replace Device_3Impl with Device_4Impl - Python : Replace Device_3Impl with Device_4Impl, PyDeviceClass with DeviceClass and PyUtil with Util. If you are quite lazy you can add this at the beginning of your *$Class.py* file (and be still parseable by Pogo): .. code-block:: python :linenos: import PyTango if 'PyUtil' not in dir(PyTango): PyTango.Device_3Impl = PyTango.Device_4Impl PyTango.PyDeviceClass = PyTango.DeviceClass PyTango.PyUtil = PyTango.Util Simplify changes by adding this line ************************************ .. code-block:: python :linenos: if 'PyUtil' not in dir(PyTango): PyTango.PyDeviceClass = PyTango.DeviceClass PyTango.PyUtil = PyTango.Util .. definitions ------------ .. _official: http://pytango.readthedocs.io/en/latest .. _download: http://www.tango-controls.org/ .. _PiPy: http://pypi.python.org/pypi/PyTango .. _PyTango: https://intranet.cells.es/Members/srubio/howto/HowToPyTango