└── fandango ├── test ├── test_device.py ├── tests.txt └── test.py ├── interface ├── __init__.py ├── inheritance.py └── CopyCatDS.py ├── scripts ├── devicemonitor ├── ctds ├── sardanact ├── tango_create_device.py ├── tango_starter.py ├── tango_create_starter.py ├── csv2tango.py └── tango2csv.py ├── debug.py ├── README ├── doc.py ├── __init__.py ├── db.py ├── excepts.py ├── devslist.py ├── web.py ├── log.py ├── callbacks.py ├── linos.py ├── device.py └── objects.py /fandango/test/test_device.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fandango/test/tests.txt: -------------------------------------------------------------------------------- 1 | 2 | Do pycheck on individual files 3 | 4 | pychecker --limit 200 device.py 2>/dev/null | grep device.py 5 | -------------------------------------------------------------------------------- /fandango/interface/__init__.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | 3 | from .inheritance import * 4 | 5 | try: 6 | from CopyCatDS import CopyCatDS,CopyCatDSClass,CopyCatServer 7 | except: 8 | print 'Unable to import fandango.interface.CopyCatDS' 9 | print traceback.format_exc() 10 | 11 | 12 | -------------------------------------------------------------------------------- /fandango/scripts/devicemonitor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | usage: devicemonitor [ ]* 5 | """ 6 | 7 | import sys 8 | import time 9 | import PyTango 10 | 11 | m = PyTango.DeviceProxy(sys.argv[1]) 12 | 13 | cb = PyTango.utils.EventCallBack() 14 | 15 | if len(sys.argv) == 2: 16 | attrs = "state", 17 | else: 18 | attrs = map(str.strip, sys.argv[2:]) 19 | 20 | attrs = map(str.strip, attrs) 21 | 22 | eids = [m.subscribe_event(attr, PyTango.EventType.CHANGE_EVENT, cb) for attr in attrs] 23 | 24 | try: 25 | while True: 26 | time.sleep(1) 27 | except KeyboardInterrupt: 28 | print "Finsihed monitoring" 29 | 30 | -------------------------------------------------------------------------------- /fandango/scripts/ctds: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import fandango 4 | import sys 5 | import time 6 | 7 | COMMANDS = 'status start stop restart'.split() 8 | 9 | args = fandango.sysargs_to_dict(sys.argv[1:],defaults=['keys'],trace=False) 10 | if not args['keys'] or len(args['keys'])<2 or args['keys'][0] not in COMMANDS: 11 | print 'USAGE: ctds [servers....]' 12 | sys.exit() 13 | 14 | action,keys = args['keys'][0],args['keys'][1:] 15 | astor = fandango.Astor() 16 | [astor.load_by_name(k) for k in keys] 17 | 18 | if action == 'status': 19 | astor.states() 20 | print astor.__repr__() 21 | elif action == 'start': 22 | astor.start_servers() 23 | elif action == 'stop': 24 | astor.stop_servers() 25 | elif action == 'restart': 26 | print('Stopping servers ...') 27 | astor.stop_servers() 28 | print('Waiting 10 s ...') 29 | time.sleep(10.) 30 | print('Starting servers ...') 31 | astor.start_servers() 32 | -------------------------------------------------------------------------------- /fandango/debug.py: -------------------------------------------------------------------------------- 1 | 2 | import traceback 3 | import threading 4 | import time 5 | 6 | def timeit(target,args=[],kwargs={},setup='pass',number=1): 7 | if isinstance(target,str): 8 | import timeit 9 | return timeit.timeit(target,setup,number=number) 10 | elif callable(target): 11 | t0 = time.time() 12 | try: 13 | [target(*args) for i in range(number)] 14 | except: 15 | traceback.print_exc() 16 | return time.time()-t0 17 | 18 | 19 | def test_xtreme(device,attribute,value): 20 | import PyTango 21 | i,dp = 0,PyTango.DeviceProxy(device) 22 | try: 23 | while i [filter]'%'|'.join(COMMANDS) 12 | sys.exit() 13 | 14 | keys = ('pool','macroserver') 15 | action,filters = args.get('action',None),args.get('filter','*') 16 | astor = fandango.Astor() 17 | [astor.load_by_name(k) for k in keys] 18 | target = sorted(k for k in astor if fandango.searchCl(filters,k)) 19 | 20 | if action == 'status': 21 | astor.states() 22 | print astor.__repr__() 23 | elif action == 'start': 24 | print('Starting %s'%target) 25 | astor.start_servers() 26 | elif action == 'stop': 27 | print('Stopping %s'%target) 28 | astor.stop_servers() 29 | elif action == 'restart': 30 | astor.stop_servers() 31 | print('Stopping %s'%target) 32 | print('Waiting 10 s ...') 33 | time.sleep(10.) 34 | print('Starting %s'%target) 35 | astor.start_servers() -------------------------------------------------------------------------------- /fandango/README: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | ## 3 | ## file : fandango 4 | ## description : Functional tools for PyTango developers, replacing PyTango_utils 5 | ## project : Tango Control System 6 | ## url : http://www.tango-controls.org/Documents/tools/Fandango 7 | ## 8 | ## Author: Sergi Rubio Manrique, srubio@cells.es 9 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 10 | ## Bellaterra 11 | ## Spain 12 | ## 13 | ############################################################################# 14 | ## 15 | ## This file is part of Tango Control System 16 | ## 17 | ## Tango Control System is free software; you can redistribute it and/or 18 | ## modify it under the terms of the GNU General Public License as published 19 | ## by the Free Software Foundation; either version 3 of the License, or 20 | ## (at your option) any later version. 21 | ## 22 | ## Tango Control System is distributed in the hope that it will be useful, 23 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | ## GNU General Public License for more details. 26 | ## 27 | ## You should have received a copy of the GNU General Public License 28 | ## along with this program; if not, see . 29 | ########################################################################### -------------------------------------------------------------------------------- /fandango/scripts/tango_create_device.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | __doc__ = """ 4 | Usage: 5 | tango_create Server/Instance Class DeviceName {properties as python dictionary} 6 | """ 7 | 8 | import sys 9 | import fandango 10 | 11 | try: 12 | server,classname,devicename = sys.argv[1:4] 13 | props = eval(fandango.first(sys.argv[4:]) or ['{}']) 14 | except: 15 | print __doc__ 16 | sys.exit(1) 17 | 18 | fandango.tango.add_new_device(server,classname,devicename) 19 | if props: 20 | fandango.get_database().put_device_property 21 | 22 | 23 | from PyTango import * 24 | 25 | db = Database() 26 | 27 | rateDS = 100 28 | nDS = 30 29 | first = 31 30 | 31 | def addTangoDev(server,_class,device): 32 | di = DbDevInfo() 33 | di.name,di._class,di.server = device,_class,server 34 | db.add_device(di) 35 | 36 | _class = 'PySignalSimulator' 37 | domain = 'sim' 38 | family = 'pysignalsimulator' 39 | 40 | print 'Creating ',str(rateDS*nDS) , ' TangoTest device Servers ...' 41 | for m in range(first,nDS+first): 42 | server = '/'.join([_class,'%02d'%m]) 43 | print 'Deleting server ',server 44 | try: db.delete_server(server) 45 | except: pass 46 | for n in range(1,rateDS+1): 47 | server = '/'.join([_class,'%02d'%m]) 48 | member = '%02d'%m+'-'+'%02d'%n 49 | device = '/'.join([domain,family,member]) 50 | print 'Creating device ',device 51 | addTangoDev(server,_class,device) 52 | print 'Adding Properties to class/device = ',_class,'/',device 53 | db.put_class_property(_class,{'Description':['device used to test the archiving system']}) 54 | db.put_device_property(device,{'SimAttributes':['A1=sin((t+random())/2.)']}) 55 | db.put_device_property(device,{'SimStates':['FAULT=square(t,period=10)',"ALARM=Attr('A1')<0",'ON=1']}) 56 | 57 | -------------------------------------------------------------------------------- /fandango/scripts/tango_starter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys,time,traceback 4 | import fandango 5 | 6 | __doc__ = """ 7 | tango_starter.py 8 | ---------------- 9 | 10 | This script uses fandango.servers.ServersDict class to start/stop devices on any point of the control system using Tango Starters. 11 | 12 | tango_starter.py [host] start|stop|restart|status [server_name_expression_list] 13 | 14 | Examples: 15 | 16 | tango_starter.py status # Will display status of devices in localhost 17 | tango_starter.py hostname.you.org stop "*hdb*" # Will stop all *hdb* devices in hostname.you.org 18 | tango_starter.py restart "pyalarm/*" # Will restart PyAlarm devices in ALL hosts 19 | """ 20 | 21 | actions = 'start stop restart status'.split() 22 | 23 | try: 24 | host = sys.argv[1] if not sys.argv[1] in actions else '' 25 | action = fandango.first([a for a in sys.argv if a in actions] or 'status') 26 | targets = sys.argv[(2 if sys.argv[1] in actions else 3):] 27 | except: 28 | print __doc__ 29 | sys.exit(1) 30 | 31 | action,targets = sys.argv[1],sys.argv[2:] 32 | try: 33 | astor = fandango.Astor() 34 | if targets: 35 | for exp in targets: 36 | print 'Loading %s devices'%exp 37 | astor.load_by_name(exp) 38 | else: 39 | h = host or fandango.MyMachine().host 40 | print 'Loading %s devices'%h 41 | astor.load_by_host(h) 42 | 43 | sts = astor.states() 44 | running = [s for s,st in sts.items() if st is not None] 45 | if action in ('status',): 46 | #if targets: 47 | #print '\n'.join(sorted('%s:\t%s'%(k,str(v)) for k,v in sts.items())) 48 | #else: 49 | for h,ls in sorted(astor.get_host_overview().items()): 50 | for l,ds in sorted(ls.items()): 51 | print '%s:%s'%(h,l) 52 | for s,devs in sorted(ds.items()): 53 | print '\t'+s 54 | for d,st in sorted(devs.items()): 55 | print '\t\t%s:\t%s'%(d,st) 56 | 57 | if action in ('stop','restart'): 58 | astor.stop_servers(running) 59 | if action in ('restart',): 60 | time.sleep(5) 61 | sts = astor.states() 62 | if action in ('start',): 63 | astor.start_servers(running,**(host and {'host':host} or {})) 64 | 65 | print '-'*80 66 | print(' '.join(sys.argv)+': Done') 67 | except: 68 | print traceback.format_exc() 69 | print '-'*80 70 | print(' '.join(sys.argv)+': Failed!') 71 | -------------------------------------------------------------------------------- /fandango/test/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys,traceback 4 | class Skip(Exception): pass 5 | 6 | #Testing fandango imports 7 | try: 8 | m = 'fandango.__all__' 9 | print 'Loading fandango ...' 10 | import fandango 11 | print 'ReleaseNumber = %s'%str(fandango.RELEASE) 12 | from fandango import * 13 | import fandango.functional as fun 14 | print '\n\n'+m+': OK' 15 | except Exception,e: 16 | traceback.print_exc() 17 | print m+': KO!' 18 | sys.exit(1) 19 | 20 | #Testing fandango.$module passed as argument 21 | a = fun.first(sys.argv[1:],'*') 22 | 23 | try: 24 | m = 'fandango.functional' 25 | if not fun.searchCl(a,m): raise Skip() 26 | 27 | assert fun.searchCl('id24eu & xbpm','id24eu_XBPM_naaa',extend=True) 28 | assert not fun.searchCl('id24eu & !xbpm','id24eu_XBPM_naaa',extend=True) 29 | assert fun.searchCl('id24eu & !xbpm','id24eu_PM_naaa',extend=True) 30 | assert not fun.searchCl('id24eu & xbpm','id24eu_PM_naaa',extend=True) 31 | assert None is fun.first([],None) 32 | assert 1 is fun.first((i for i in range(1,3))) 33 | assert 2 is fun.last((i for i in range(1,3))) 34 | assert 0 is fun.last([],default=0) 35 | print m+': OK' 36 | except Skip:pass 37 | except Exception,e: 38 | traceback.print_exc() 39 | print m+': KO!' 40 | sys.exit(1) 41 | 42 | try: 43 | m = 'fandango.excepts' 44 | if not fun.searchCl(a,m): raise Skip() 45 | import fandango.excepts as f_excepts 46 | assert 1 == f_excepts.trial(lambda:1/1,lambda e:10,return_exception=True) 47 | assert 10 == f_excepts.trial(lambda:1/0,lambda e:10,return_exception=True) 48 | print m+': OK' 49 | except Skip:pass 50 | except Exception,e: 51 | traceback.print_exc() 52 | print m+': KO!' 53 | sys.exit(1) 54 | 55 | try: 56 | m = 'fandango.tango' 57 | if not fun.searchCl(a,m): raise Skip() 58 | import fandango.tango as f_tango 59 | assert isinstance(f_tango.get_proxy('sys/database/2'),fandango.tango.PyTango.DeviceProxy) 60 | assert isinstance(f_tango.get_proxy('sys/database/2/state'),fandango.tango.PyTango.AttributeProxy) 61 | assert isinstance(f_tango.TGet(),fandango.tango.PyTango.Database) 62 | assert isinstance(f_tango.TGet(f_tango.TGet('sys/database/*')[0]),fandango.tango.PyTango.DeviceProxy) 63 | assert isinstance(f_tango.TGet(f_tango.TGet('sys/database/*/st[a]te')[0]),int) 64 | assert f_tango.get_device_info('sys/database/2').dev_class == 'DataBase' 65 | assert fandango.isNaN(f_tango.TangoEval(trace=False).eval('da/do/di/du',_raise=fandango.NaN)) 66 | assert fandango.tango.TangoEval(trace=False).eval('sys/database/2',_raise=None) in (0,None) 67 | print m+': OK' 68 | except Skip:pass 69 | except Exception,e: 70 | traceback.print_exc() 71 | print m+': KO!' 72 | sys.exit(1) 73 | 74 | sys.exit(0) 75 | -------------------------------------------------------------------------------- /fandango/scripts/tango_create_starter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | print 'An updated version of this script is included as createStarter function in csv2tango' 4 | 5 | from PyTango import * 6 | import sys 7 | db = Database() 8 | 9 | def addStarterDev(host): 10 | server = 'Starter'; instance = host.split('.')[0]; member = instance 11 | sep = '/'; domain = 'tango'; family = 'admin'; 12 | di = DbDevInfo() 13 | di.name,di._class,di.server = \ 14 | sep.join([domain,family,member]), \ 15 | server,sep.join([server,instance]) 16 | db.add_device(di) 17 | 18 | hostnames = [] 19 | if len(sys.argv)>1: 20 | for h in sys.argv[1:]: 21 | hostnames.append(h) 22 | else: 23 | hostnames = ['palantir01'] 24 | 25 | for h in hostnames: 26 | addStarterDev(h) 27 | 28 | """ 29 | Class Properties 30 | Property name 31 | 32 | Property type 33 | 34 | Description 35 | ReadInfoDbPeriod Tango::DEV_SHORT Period to read database for new info if not fired from Database server. 36 | NbStartupLevels Tango::DEV_SHORT Number of startup levels managed by starter. 37 | CmdPollingTimeout Tango::DEV_LONG Timeout value in seconds to stop polling if no command has been received. 38 | UseEvents Tango::DEV_SHORT Use events if not null. 39 | ServerStartupTimeout Tango::DEV_LONG Timeout on device server startup in seconds. 40 | """ 41 | 42 | dclass = 'Starter' 43 | #db.put_class_property(dclass,{'ReadInfoDbPeriod':[60]}) 44 | db.put_class_property(dclass,{'NbStartupLevels':[10]}) 45 | #db.put_class_property(dclass,{'CmdPollingTimeout':[10]}) 46 | #db.put_class_property(dclass,{'UseEvents':[1]}) 47 | #db.put_class_property(dclass,{'ServerStartupTimeout':[60]}) 48 | 49 | """ 50 | Device Properties 51 | Property name 52 | 53 | Property type 54 | 55 | Description 56 | StartDsPath Array of string Path to find executable files to start device servers 57 | WaitForDriverStartup Tango::DEV_SHORT The Starter will wait a bit before starting servers, to be sure than the drivers are started.This time is in seconds. 58 | UseEvents Tango::DEV_SHORT Use events if not null. 59 | StartServersAtStartup Tango::DEV_BOOLEAN Skip starting servers at startup if false. 60 | InterStartupLevelWait Tango::DEV_LONG Time to wait before two startup levels in seconds. 61 | ServerStartupTimeout Tango::DEV_LONG Timeout on device server startup in seconds. 62 | """ 63 | 64 | for dname in ['tango/admin/'+h.split('.')[0] for h in hostnames]: 65 | print 'Initial values for properties of ',dname,' are ', \ 66 | db.get_device_property(dname,['StartDsPath','WaitForDriverStartup','UseEvents','StartServersAtStartup','InterStartupLevelWait','ServerStartupTimeout']) 67 | db.put_device_property(dname,{'StartDsPath':[ 68 | '~/devservers', 69 | '~/ds', 70 | '~/tango/bin', 71 | '~/archiving/bin', 72 | ]}) 73 | #db.put_device_property(dname,{'WaitForDriverStartup':[60]}) 74 | #db.put_device_property(dname,{'UseEvents':[1]}) 75 | db.put_device_property(dname,{'StartServersAtStartup':['True']}) 76 | db.put_device_property(dname,{'InterStartupLevelWait':[30]}) 77 | #db.put_device_property(dname,{'ServerStartupTimeout':[60]}) 78 | print 'Final values for properties of ',dname,' are ', \ 79 | db.get_device_property(dname,['StartDsPath','WaitForDriverStartup','UseEvents','StartServersAtStartup','InterStartupLevelWait','ServerStartupTimeout']) 80 | 81 | 82 | -------------------------------------------------------------------------------- /fandango/doc.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | this module will be used to document lots of things with sphinx 4 | 5 | Just do that at the end of your modules: 6 | 7 | from fandango.doc import get_autodoc 8 | __doc__ = get_autodoc(__name__,vars()) 9 | 10 | """ 11 | 12 | import inspect,fandango,os 13 | 14 | __all__ = ['get_autodoc','get_class_docs','get_function_docs','get_vars_docs'] 15 | 16 | DEFAULT_MODULE = """ 17 | %s 18 | %s 19 | 20 | .. automodule:: %s 21 | 22 | """ 23 | 24 | DEFAULT_VARIABLE = """ 25 | 26 | .. autodata:: %s.%s 27 | :annotation: 28 | 29 | """ 30 | 31 | RAW_VARIABLE = """ 32 | 33 | .. autodata:: %s.%s 34 | 35 | """ 36 | 37 | DEFAULT_FUNCTION = """ 38 | 39 | .. autofunction:: %s.%s 40 | 41 | """ 42 | 43 | DEFAULT_CLASS = """ 44 | 45 | .. autoclass:: %s.%s 46 | :members: 47 | 48 | """ 49 | 50 | TM0 = '=',True 51 | TM1 = '=',False 52 | TM2 = '-',False 53 | TM3 = '~',False 54 | TM4 = '.',False 55 | 56 | def generate_rest_files(module,path='source'): 57 | print '\n'*5 58 | print 'Writing documentation settings to %s/*rst' % (path) 59 | if fandango.isString(module): module = fandango.loadModule(module) 60 | submodules = [(o,v) for o,v in vars(module).items() 61 | if inspect.ismodule(v) and v.__name__.startswith(module.__name__)] 62 | 63 | for o,v in submodules: 64 | filename = path+'/'+o+'.rst' 65 | if not os.path.isfile(filename): 66 | print('writing %s'%filename) 67 | open(filename,'w').write(DEFAULT_MODULE%(v.__name__,'='*len(v.__name__),v.__name__)) 68 | 69 | 70 | print('\nWrite this into index.rst:\n') 71 | print(""" 72 | .. toctree:: 73 | :maxdepth: 2 74 | 75 | """+ 76 | '\n '.join([t[0] for t in submodules])) 77 | 78 | def get_rest_title(string,char='=',double_line=False): 79 | txt = char*len(string) if double_line else '' 80 | txt += '\n'+string+'\n' 81 | txt += char*len(string) 82 | txt += '\n\n' 83 | return txt 84 | 85 | def get_vars_docs(module_name,module_vars,title='Variables',subtitle=True): 86 | defs = [(get_rest_title(f,*TM3) if subtitle else '')+DEFAULT_VARIABLE%(module_name,f) 87 | for f in module_vars] 88 | if defs: 89 | return '\n'+get_rest_title(title,*TM2)+'\n'.join(defs) 90 | else: 91 | return '' 92 | 93 | def get_function_docs(module_name,module_vars,title='Functions',subtitle=True): 94 | functions = [(f,v) for f,v in module_vars.items() 95 | if inspect.isfunction(v) and v.__module__==module_name] 96 | defs = [(get_rest_title(f[0],*TM3) if subtitle else '')+DEFAULT_FUNCTION%(module_name,f[0]) 97 | for f in functions] 98 | if defs: 99 | return '\n'+get_rest_title(title,*TM2)+'\n'.join(defs) 100 | else: 101 | return '' 102 | 103 | def get_class_docs(module_name,module_vars,title='Classes',subtitle=True): 104 | classes = [(f,v) for f,v in module_vars.items() 105 | if inspect.isclass(v) and v.__module__==module_name] 106 | defs = [(get_rest_title(f[0],*TM3) if subtitle else '')+DEFAULT_CLASS%(module_name,f[0]) 107 | for f in classes] 108 | if defs: 109 | return '\n'+get_rest_title(title,*TM2)+'\n'.join(defs) 110 | else: 111 | return '' 112 | 113 | def get_autodoc(module_name,module_scope,module_vars=[],module_doc='',module_postdoc='\n----\n'): 114 | """ 115 | from fandango import get_autodoc 116 | __doc__ = get_autodoc(__name__,vars()) 117 | """ 118 | module_doc = module_doc or module_scope.get('__doc__','') or '' 119 | #if not module_doc: 120 | #module_doc = get_rest_title(module_name,'=',double_line=True) 121 | #if ".. auto" not in m.__doc__: 122 | if module_vars: 123 | module_doc += get_vars_docs(module_name,module_vars) 124 | module_doc += get_class_docs(module_name,module_scope) 125 | module_doc += get_function_docs(module_name,module_scope) 126 | return module_doc+module_postdoc 127 | 128 | if __name__ == '__main__': 129 | import sys 130 | generate_rest_files(sys.argv[1]) 131 | 132 | __doc__ = get_autodoc(__name__,vars()) 133 | -------------------------------------------------------------------------------- /fandango/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | 3 | ############################################################################# 4 | ## 5 | ## file : PyTango_utils 6 | ## 7 | ## description : This module includes some PyTango additional classes and methods that 8 | ## are not implemented in the C++ API. Some of them will be moved to the official 9 | ## TAU Core packages in the future. 10 | ## 11 | ## project : Tango Control System 12 | ## 13 | ## $Author: Sergi Rubio Manrique, srubio@cells.es $ 14 | ## 15 | ## $Revision: Created on 19th February 2008 $ 16 | ## 17 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 18 | ## Bellaterra 19 | ## Spain 20 | ## 21 | ############################################################################# 22 | ## 23 | ## This file is part of Tango Control System 24 | ## 25 | ## Tango Control System is free software; you can redistribute it and/or 26 | ## modify it under the terms of the GNU General Public License as published 27 | ## by the Free Software Foundation; either version 3 of the License, or 28 | ## (at your option) any later version. 29 | ## 30 | ## Tango Control System is distributed in the hope that it will be useful, 31 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | ## GNU General Public License for more details. 34 | ## 35 | ## You should have received a copy of the GNU General Public License 36 | ## along with this program; if not, see . 37 | ########################################################################### 38 | 39 | """ 40 | @package fandango 41 | @mainpage fandango "Functional tools for Tango" Reference 42 | Several modules included are used in Tango Device Server projects, like @link dynamic @endlink and PyPLC. @n 43 | @brief This module(s) include some PyTango additional classes and methods that are not implemented in the C++/Python API's; it replaces the previous PyTango_utils module 44 | """ 45 | 46 | import os,traceback 47 | try: 48 | import objects,imp 49 | PATH = os.path.dirname(objects.__file__) 50 | ReleaseNumber = type('ReleaseNumber',(tuple,),{'__repr__':(lambda self:'.'.join(('%02d'%i for i in self)))}) 51 | #print 'Fandango Release number: %s, loaded from %s/CHANGES' % (ReleaseNumber,PATH) 52 | RELEASE = ReleaseNumber(imp.load_source('changelog',PATH+'/CHANGES').RELEASE) 53 | except Exception,e: 54 | print traceback.format_exc() 55 | print 'Unable to load RELEASE number: %s'%e 56 | 57 | try: 58 | from functional import * 59 | except Exception,e: print 'Unable to import functional module: %s'%e 60 | 61 | try: 62 | from log import printf,Logger,LogFilter,shortstr,except2str,FakeLogger 63 | except Exception,e: print 'Unable to import log module: %s'%e 64 | 65 | try: 66 | from excepts import trial,getLastException,getPreviousExceptions,ExceptionWrapper,Catched,CatchedArgs 67 | except: print 'Unable to import excepts module' 68 | 69 | try: 70 | from objects import Object,Singleton,SingletonMap,Struct,NamedProperty 71 | from objects import dirModule,loadModule,dirClasses,obj2dict 72 | from objects import Decorator,ClassDecorator,Decorated,BoundDecorator 73 | except Exception,e: 74 | print 'Unable to import objects module: %s'%traceback.format_exc() 75 | 76 | try: 77 | from linos import shell_command,ping,sysargs_to_dict,listdir,sendmail,MyMachine 78 | except: print 'Unable to import linos module: %s\n'%traceback.format_exc() 79 | 80 | try: 81 | from arrays import CSVArray 82 | except: print 'Unable to import arrays module' 83 | 84 | try: 85 | from doc import * 86 | except: print('Unable to import doc module') 87 | 88 | try: 89 | from dicts import ThreadDict,CaselessDict,ReversibleDict,CaselessDefaultDict,DefaultThreadDict,Enumeration,SortedDict,CaselessList 90 | from dicts import defaultdict,defaultdict_fromkey,fuzzyDict,reversedict,collections 91 | except: print 'Unable to import dicts module' 92 | 93 | try: 94 | from threads import WorkerProcess,WorkerThread,SingletonWorker 95 | except: print 'Unable to import threads module' 96 | 97 | #TANGO related modules 98 | try: 99 | from tango import TGet,get_device,get_database,get_database_device,get_all_devices,get_device_info,\ 100 | get_alias_for_device,get_device_for_alias,get_tango_host, \ 101 | find_devices,find_attributes,get_matching_devices,get_matching_attributes,\ 102 | cast_tango_type,parse_tango_model,check_attribute,check_device,except2str,\ 103 | TangoEval,ProxiesDict,getTangoValue,TangoCommand,fakeEvent,fakeEventType 104 | try: 105 | from device import Dev4Tango,TangoEval,TimedQueue,DevChild,TangoCommand 106 | except Exception,e: raise Exception('fandango.device: %s'%e) 107 | try: 108 | from servers import ServersDict,Astor,ProxiesDict,ComposersDict 109 | except Exception,e: raise Exception('fandango.servers: %s'%e) 110 | try: 111 | path = imp.find_module('fandango')[1] 112 | deprecated = [f for f in (os.path.join(path,'interface'+ext) for ext in ('.py','.pyc','.pyo','py~')) if os.path.exists(f)] 113 | if deprecated: 114 | print 'fandango.interface: deprecated files still exist: %s'%','.join(deprecated) 115 | try: [os.remove(f) for f in deprecated] 116 | except: print('... and should be removed manually!') 117 | from interface import FullTangoInheritance,NewTypeInheritance 118 | except Exception,e: raise Exception('fandango.interface: %s'%e) 119 | try: 120 | from dynamic import DynamicDS,DynamicDSClass,DynamicAttribute,DynamicDSTypes,CreateDynamicCommands,DynamicServer 121 | except Exception,e: raise Exception('fandango.dynamic: %s'%e) 122 | except Exception,e: 123 | print 'Unable to import fandango.*tango modules: %s'%e 124 | #print traceback.format_exc() 125 | 126 | #OTHER fancy modules 127 | if False: #Disabled to avoid extra dependencies!! 128 | try: import web 129 | except: print 'Unable to import fandango.web module' 130 | try: import qt 131 | except: print 'Unable to import fandango.qt module' 132 | try: from db import FriendlyDB 133 | except: print 'Unable to import db module' 134 | 135 | __all__ = ['dicts','excepts','log','objects','db','device','web','threads','dynamic','callbacks','arrays','servers','linos','functional','interface','qt'] 136 | #print 'module reloaded' 137 | -------------------------------------------------------------------------------- /fandango/scripts/csv2tango.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | Script to import CSV Files to Tango Database 5 | srubio@cells.es, 2008 6 | ALBA Synchrotron Controls Group 7 | """ 8 | 9 | import sys,os, traceback 10 | from PyTango import * 11 | 12 | sys.path.append('..') 13 | 14 | import fandango 15 | #import fandango.csv 16 | from fandango.arrays import CSVArray 17 | 18 | if len(sys.argv)<1: 19 | exit() 20 | 21 | print 'Loading file ...' 22 | array = CSVArray() 23 | array.load(sys.argv[1],prune_empty_lines=True) 24 | array.setOffset(x=1) 25 | for s in ['server','class','device','property']: array.fill(head=s) 26 | 27 | #print array.get(x=0) 28 | print 'Getting device column ...' 29 | answer = raw_input('Do you want to force all device names to be uppercase? (Yes/No)') 30 | if answer.lower().startswith('y'): 31 | listofdevs = {} 32 | [listofdevs.__setitem__(s.upper(),i) for s,i in array.get(head='device',distinct=True).iteritems()] 33 | else: 34 | listofdevs = array.get(head='device',distinct=True) 35 | print '%s devices read from file.' % len(listofdevs) 36 | lkeys = listofdevs.keys() 37 | lkeys.sort() 38 | 39 | ####################################################################### 40 | 41 | def getProperties(device): 42 | props={} 43 | for l in listofdevs[device]: 44 | pr = array.get(x=l,head='property') 45 | try: 46 | if pr not in props: 47 | props[pr]=[array.get(x=l,head='value')] 48 | else: 49 | props[pr].append(array.get(x=l,head='value')) 50 | except Exception,e: 51 | print traceback.format_exc() 52 | print 'Failed to get %s property' % pr 53 | for p,v in props.items(): 54 | if '\\n' in v[0]: 55 | print '------------------------rebuilding property ',p,'-----------------------------' 56 | props[p]='\\n'.join(v).split('\\n') 57 | return props 58 | 59 | def getField(line,field): 60 | server=array.get(x=line,head=field) 61 | _class=array.get(x=listofdevs[device][0],head='class') 62 | return server,_class,name 63 | 64 | def createStarter(hostnames,folders=[]): 65 | """ 66 | TODO: createStarter imported from createStater.py but not implemented yet in the script 67 | """ 68 | db = Database() 69 | def addStarterDev(host): 70 | server = 'Starter'; instance = host.split('.')[0]; member = instance 71 | sep = '/'; domain = 'tango'; family = 'admin'; 72 | di = DbDevInfo() 73 | di.name,di._class,di.server = \ 74 | sep.join([domain,family,member]), \ 75 | server,sep.join([server,instance]) 76 | db.add_device(di) 77 | for h in hostnames: 78 | addStarterDev(h) 79 | 80 | ## Setting Starter Class properties 81 | dclass = 'Starter' 82 | #db.put_class_property(dclass,{'ReadInfoDbPeriod':[60]}) 83 | db.put_class_property(dclass,{'NbStartupLevels':[10]}) 84 | #db.put_class_property(dclass,{'CmdPollingTimeout':[10]}) 85 | #db.put_class_property(dclass,{'UseEvents':[1]}) 86 | #db.put_class_property(dclass,{'ServerStartupTimeout':[60]}) 87 | 88 | ## Setting Starter Device Properties 89 | folders = folders or ['~/devservers','~/ds','~/tango/bin','~/archiving/bin',] 90 | for dname in ['tango/admin/'+h.split('.')[0] for h in hostnames]: 91 | print 'Initial values for properties of ',dname,' are ', \ 92 | db.get_device_property(dname,['StartDsPath','WaitForDriverStartup','UseEvents','StartServersAtStartup','InterStartupLevelWait','ServerStartupTimeout']) 93 | 94 | db.put_device_property(dname,{'StartDsPath':folders}) 95 | #db.put_device_property(dname,{'WaitForDriverStartup':[60]}) 96 | #db.put_device_property(dname,{'UseEvents':[1]}) 97 | db.put_device_property(dname,{'StartServersAtStartup':['True']}) 98 | db.put_device_property(dname,{'InterStartupLevelWait':[30]}) 99 | #db.put_device_property(dname,{'ServerStartupTimeout':[60]}) 100 | 101 | print 'Final values for properties of ',dname,' are ', \ 102 | db.get_device_property(dname,['StartDsPath','WaitForDriverStartup','UseEvents','StartServersAtStartup','InterStartupLevelWait','ServerStartupTimeout']) 103 | 104 | print createStarter.__doc__ 105 | ####################################################################### 106 | 107 | print 'Get device properties ...' 108 | for device in listofdevs.keys(): 109 | print 'Device ',device,' properties are:' 110 | props=getProperties(device) 111 | for p,v in props.items(): print '\t',p,':',str(v) 112 | 113 | def addTangoDev(server,_class,device): 114 | di = DbDevInfo() 115 | di.name,di._class,di.server = device,_class,server 116 | db.add_device(di) 117 | 118 | overwrite = False 119 | answer = raw_input('do you want to effectively add this devices and properties to the database %s? (Yes/No/Overwrite)'%os.environ['TANGO_HOST']) 120 | 121 | if answer.lower() in ['o','overwrite']: 122 | overwrite,answer = True,'yes' 123 | 124 | if answer.lower() in ['y','yes']: 125 | db = Database() 126 | for device,lines in listofdevs.items(): 127 | server=array.get(x=lines[0],head='server').strip() 128 | _class=array.get(x=lines[0],head='class').strip() 129 | 130 | # Create the Device if it doesn't exists 131 | #---------------------------------------------------------------------------------------- 132 | if not db.get_device_member(device) or overwrite: 133 | print 'Creating ',device,' device ...' 134 | addTangoDev(server,_class,device) 135 | else: 136 | print device,' device already exists ...' 137 | 138 | props=getProperties(device) 139 | oldprops=db.get_device_property(device,props.keys()) 140 | 141 | ##@todo loading default properties 142 | # A way to define easily if some default properties must be defined and implemented here! 143 | 144 | # oldprops values must be updated with new values 145 | oldprops=db.get_device_property(device,props.keys()) 146 | 147 | # Set properties in the Database (overwriting iff specified) 148 | #---------------------------------------------------------------------------------------- 149 | for property,value in props.items(): 150 | if overwrite or not oldprops[property]: 151 | value = type(value) in (list,set) and value or [value] 152 | while not value[-1] and len(value)>1: value.pop(-1) 153 | print 'Device Property ',device,'.',property,', creating: ',value 154 | db.put_device_property(device,{property:value}) 155 | else: 156 | print 'Device Property ',device,'.',property,' already exists: ',oldprops[property] 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /fandango/scripts/tango2csv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | __usage__ = """ 4 | tango2csv 5 | This script allows to extract all the device servers matching the given filters. 6 | The arguments are: 7 | ./tango2csv filter1 filter2 filter3 filter4 ... destination.csv 8 | 9 | Sergi Rubio, srubio@cells.es, 2008 10 | """ 11 | 12 | import sys 13 | import traceback 14 | import re 15 | from PyTango import * 16 | from fandango.arrays import CSVArray 17 | from fandango.servers import ServersDict 18 | 19 | """ 20 | Search: Domain/Family/Member[/Attribute] (QLineEdit) 21 | It must allow wildcards (*) to select any device or attribute from the database. 22 | The database allows to search only for devices ... so the search_string must be split in 23 | dev_name and att_name 24 | search_string='domain/family/member/attribute' 25 | search_string,att_search = search_string.rsplit('/',1) if search_string.count('/')>2 else (search_string,'') 26 | 27 | Results: List of d/f/m or d/f/m/a names (A text widget with multiple lines and scrollable) 28 | It will show all the device or attribute names matching with the Search string. 29 | It must allow multiple (shift/ctrl+mouse_button) selection of lines 30 | How to get the list of running devices from Database: 31 | PyTango.Database().get_device_exported('*') or .get_device_exported(search_string) 32 | How to get the list of attributes from the Device name: 33 | import re 34 | try: 35 | atts_list=[a.name for a in PyTango.DeviceProxy(dev_name).attribute_list_query() if not att_search or re.match(att_search.replace('*','.*'),a.name)] 36 | except Exception,e: 37 | print 'Unable to contact with device %s: %s'%(dev_name,str(e)) 38 | atts_list=[] 39 | 40 | And two buttons: 41 | Open/Add: It will return to the main application the list of selected items in Results widget 42 | 43 | Future improvements: 44 | List filtering: 45 | Add a list of regular expressions or allowed names as an argument for Widgets creation 46 | The widget will show only those names that match: 47 | import re 48 | if any(re.match(reg,item_name) for reg in reg_exps): show_item(item_name) 49 | """ 50 | 51 | def isregexp(s): 52 | return any(c in s for c in ['.','[','(','{','|','^']) 53 | 54 | def get_all_devices(db=None): 55 | if not db: db = PyTango.Database() 56 | all_devs = [] 57 | doms = dict.fromkeys(db.get_device_domain('*')) 58 | for d in doms: 59 | doms[d] = dict.fromkeys(db.get_device_family(d+'/*')) 60 | for f in doms[d]: 61 | doms[d][f] = db.get_device_member(d+'/'+f+'/*') 62 | [all_devs.append('/'.join([d,f,m])) for m in doms[d][f]] 63 | return all_devs #doms 64 | 65 | def get_filtered_devs(filters): 66 | for f in filters: 67 | 68 | domain,family,member = f.split('/') if '/' in f else (f,f,f) 69 | if not isregexp(domain) and '*' not in domain: domain = '*'+domain+'*' 70 | if not isregexp(family) and '*' not in family: family = '*'+family+'*' 71 | if not isregexp(member) and '*' not in member: member = '*'+member+'*' 72 | domains = db.get_device_domain(domain) 73 | for dom in domains: 74 | families = db.get_device_family(dom+'/'+family) 75 | for fam in families: 76 | members = db.get_device_member(dom+'/'+fam+'/'+('*' if isregexp(member) else member)) 77 | #TODO: unfinished 78 | return 79 | 80 | def add_properties_to_csv(out_csv,counter,server,klass,device,properties): 81 | all_values = sum(len(v) for v in properties.values()) 82 | if out_csv.size()[0]<(counter+all_values): 83 | out_csv.resize(1+counter+all_values,out_csv.ncols) 84 | out_csv.set(counter,0,server) 85 | out_csv.set(counter,1,klass) 86 | out_csv.set(counter,2,device) 87 | for prop,values in properties.items(): 88 | if prop in ( 89 | '__SubDevices','polled_attr','polled_cmd', 90 | 'doc_url','ProjectTitle','cvs_location','Description','InheritedFrom'): continue 91 | print '%s.%s = %s' % (device,prop,values) 92 | out_csv.set(counter,3,prop) 93 | for value in values: 94 | out_csv.set(counter,4,value) 95 | print 'Added at %d: %s,%s,%s,%s' % (counter,server,device,prop,value) 96 | counter += 1 97 | return counter 98 | 99 | if __name__ == '__main__': 100 | try: 101 | print sys.argv 102 | filters = sys.argv[1:-1] 103 | if not filters: 104 | print __usage__ #'Syntax is ./tango2csv filter1 [more filters] destination.csv' 105 | exit() 106 | out_file = sys.argv[-1]# if len(sys.argv)>1 else 'output.csv' 107 | 108 | """ 109 | How to do it? 110 | if / in filter : split('/') and use each filter independently 111 | The filter could be an string ASF, a dom/fam/mem, something like *PLC* or re/re/re or re 112 | if *PLC* and not . in filter: apply directly to db 113 | if any(.,(,^,),[,]) in filter ... try to get all and apply regular expressions 114 | if ASF and not *; db('*'+filter+'*') 115 | """ 116 | 117 | db = Database() 118 | all_devs = get_all_devices(db) 119 | devices = [] 120 | for f in filters: 121 | if '*' in f and not isregexp(f): 122 | f=f.replace('*','.*') 123 | if isregexp(f): 124 | devices+=[d.lower() for d in all_devs if re.match(f.lower(),d.lower()) and ('dserver' in f or not d.startswith('dserver'))] 125 | else: 126 | devices+=[d.lower() for d in all_devs if f.lower() in d.lower() and ('dserver' in f or not d.startswith('dserver'))] 127 | 128 | print 'Creating %s file for TangoProperties configuration for %d devices' % (out_file,len(devices)) 129 | out_csv = CSVArray(out_file) 130 | out_csv.resize(1,1) #It cleans the content of the file 131 | out_csv.save() #It is really needed! 132 | out_csv.resize(len(devices),5) 133 | [out_csv.set(0,i,['server','class','device','property','value'][i]) for i in range(5)] 134 | counter = 1 135 | 136 | print ('Creating a dict with servers/classes/devices from the database ...') 137 | servers = ServersDict() 138 | klasses = {} 139 | 140 | print ('Generating output file ...') 141 | for device in sorted(devices): 142 | servers.load_by_name(device) 143 | server = servers.get_device_server(device) 144 | klass = servers.get_device_class(device,server) 145 | #Adding a Class to the file 146 | if klass not in klasses: 147 | property_names = list(db.get_class_property_list(klass)) 148 | if len(property_names): 149 | print 'Adding %s class properties'%klass 150 | klasses[klass] = db.get_class_property(klass,property_names) 151 | counter = add_properties_to_csv(out_csv,counter,klass,klass,'#CLASS',klasses[klass]) 152 | #Adding a device to the file 153 | property_names = list(db.get_device_property_list(device,'*')) 154 | if property_names: 155 | print '%d: reading device %s properties: %s' % (counter,device,property_names) 156 | properties = db.get_device_property(device,property_names) 157 | else: properties = {} 158 | counter = add_properties_to_csv(out_csv,counter,server,klass,device,properties) 159 | out_csv.save() 160 | 161 | 162 | except Exception,e: 163 | print '-------> An unforeseen exception occured....',traceback.format_exc() 164 | -------------------------------------------------------------------------------- /fandango/interface/inheritance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | """ 3 | @if gnuheader 4 | ############################################################################# 5 | ## 6 | ## file : interface.py 7 | ## 8 | ## description : see below 9 | ## 10 | ## project : Tango Control System 11 | ## 12 | ## $Author: Sergi Rubio Manrique, srubio@cells.es $ 13 | ## 14 | ## $Revision: 2008 $ 15 | ## 16 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 17 | ## Bellaterra 18 | ## Spain 19 | ## 20 | ############################################################################# 21 | ## 22 | ## This file is part of Tango Control System 23 | ## 24 | ## Tango Control System is free software; you can redistribute it and/or 25 | ## modify it under the terms of the GNU General Public License as published 26 | ## by the Free Software Foundation; either version 3 of the License, or 27 | ## (at your option) any later version. 28 | ## 29 | ## Tango Control System is distributed in the hope that it will be useful, 30 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | ## GNU General Public License for more details. 33 | ## 34 | ## You should have received a copy of the GNU General Public License 35 | ## along with this program; if not, see . 36 | ########################################################################### 37 | # Adding TRUE DeviceServer Inheritance 38 | # Searchs all dicts in the ChildClass and updates its values using the Parent dicts 39 | @endif 40 | 41 | @package interface 42 | @author srubio@cells.es 43 | 44 |

Proposals of implementation

45 |

CommsDev

46 |

47 | Hypothetical Abstract Class for Devices using Serial Line, Tcp Socket, Modbus, ... 48 |

49 |

Properties

50 |
str[] CommsDev__Properties
51 | Allows to setup the properties of the device
52 | ['key:value','key:value','key:[values]',...] 53 |
54 |

Attributes

55 |
int LastComm
56 | The last time that a communication was successful. 57 |
58 | 59 |
state ChannelState
60 | State of the hardware device on charge of communications 61 | [ON/OFF/FAULT/UNKNOWN/RUNNING] 62 |
63 |

Commands

64 |
str Talk(str)
65 | A direct raw writing/reading to the communication port 66 |
67 | 68 |

Example of making a Controller device with embedded Serial communications

69 |
 70 |         # Adding TRUE DeviceServer Inheritance
 71 |         from interface import FullTangoInheritance
 72 |         VacuumController,VacuumControllerClass = FullTangoInheritance('VacuumController', \
 73 |             VacuumController,VacuumControllerClass, \
 74 |             SerialDS,SerialDSClass,ForceDevImpl=True)
 75 |         py.add_TgClass(VacuumController,VacuumControllerClass,'VacuumController')    
 76 |     
77 | 78 | """ 79 | 80 | def getDeviceClassObjects(classname,path=''): 81 | """ 82 | This method locates a Tango Device Class in the python Path and returns its (Device,DeviceClass) objects 83 | """ 84 | import imp 85 | desc = imp.find_module(classname) 86 | print('getDeviceClassObjects(%s) from %s'%(classname,desc)) 87 | module = imp.load_module(classname,*desc) 88 | return getattr(module,classname),getattr(module,classname+'Class') 89 | 90 | def addAllClasses(obj,servername='',db=None,classes=None): 91 | """ 92 | This method will add all classes listed in the database to the server object (if those classes are available in the pythonpath) 93 | 94 | obj argument must be a PyTango.Util(['exec_name','instance_name']) object 95 | If servername or db are not specified will be taken from obj.instance() 96 | 97 | Dependences can be added to pythonpath using sys.path.insert(0,'path/to/your/device') 98 | """ 99 | if classes is None: 100 | instance = obj.instance() 101 | if not db:db = instance.get_database() 102 | if not servername: servername = instance.get_ds_name() 103 | if ' ' in servername or '.py' in servername: servename = '/'.join(s.replace('.py','').strip() for s in servername.split()) 104 | classes = db.get_server_class_list(servername) 105 | for c in classes: 106 | dev,devclass = getDeviceClassObjects(c) 107 | print('Adding %s,%s to %s'%(dev,devclass,obj)) 108 | obj.add_TgClass(devclass,dev,c) 109 | return obj 110 | 111 | 112 | def updateChildClassDicts(Child,Parent,Exclude=[]): 113 | print 'Updating %s from %s'%(Child.__name__,Parent.__name__) 114 | for attribute in dir(Child): 115 | cattr = getattr(Child,attribute) 116 | if (not attribute.startswith('__')) and isinstance(cattr,dict) and hasattr(Parent,attribute): 117 | pattr = getattr(Parent,attribute) 118 | if isinstance(pattr,dict): 119 | print 'Updating %s.%s from %s'%(Child.__name__,attribute,Parent.__name__) 120 | cattr.update((k,v) for k,v in pattr.items() if k not in Exclude and k not in cattr) 121 | #updateChildClassDicts(AlbaPLCClass,PyPLCClass) 122 | 123 | def NewTypeInheritance(name,klass,parent,dikt={}): 124 | """ Based on NewClass = type('nameOfClass',(bases,),dikt={}) """ 125 | return type(name,(klass,parent),dikt) 126 | 127 | def FullTangoInheritance(name,child,childClass,parent,parentClass,Exclude=[],ForceDevImpl=False): 128 | """ arguments are: NewDeviceName, Device, DeviceClass, newParentDevice, newParentDeviceClass, parent members to exclude 129 | @remark The code must be added always before each add_TgClass call (it could be outside the class definition file!) 130 | 131 | example: 132 |
133 |         # Adding TRUE DeviceServer Inheritance
134 |         from interface import FullTangoInheritance
135 |         PySignalSimulator,PySignalSimulatorClass = FullTangoInheritance('PySignalSimulator', \
136 |             PySignalSimulator,PySignalSimulatorClass, \
137 |             DynamicDS,DynamicDSClass,ForceDevImpl=True)
138 |         py.add_TgClass(PySignalSimulatorClass,PySignalSimulator,'PySignalSimulator')    
139 |     
140 | """ 141 | if parent not in child.__bases__: 142 | print 'Applying FullTangoInheritance from %s and %s' % (child, parent) 143 | if not ForceDevImpl: 144 | newdevice = type(name,(child,parent),{}) 145 | newdeviceclass = type(name+'Class',(childClass,parentClass),{}) 146 | else: 147 | import PyTango 148 | newdevice = type(name,(child,parent,PyTango.Device_4Impl),{}) 149 | newdeviceclass = type(name+'Class',(childClass,parentClass,PyTango.DeviceClass),{}) 150 | updateChildClassDicts(newdeviceclass,parentClass,Exclude) 151 | return newdevice,newdeviceclass 152 | else: 153 | return child,childClass 154 | 155 | def DeviceClassInheritance(ChildClass): 156 | """ class decorator """ 157 | for b in ChildClass.__bases__: 158 | for p in ('class_property_list','device_property_list','cmd_list','attr_list'): 159 | d = getattr(b,p,{}) 160 | d and getattr(ChildClass,p).update((k,v) for k,v in d.items() if k not in getattr(ChildClass,p)) 161 | return ChildClass 162 | 163 | def addTangoInterfaces(device,interfaces): 164 | """ It adds properties and implementation to the parent class from a list of tuples (Device,DeviceClass) 165 | @param device A tuple (Device,DeviceClass) to be extended with interfaces 166 | @param interfaces A list of tuples (Device,DeviceClass) to the first server 167 | @return Returns a tuple (Device,DeviceClass) containing the new interfaces 168 | 169 | from interface import FullTangoInheritance 170 | PySignalSimulator,PySignalSimulatorClass = FullTangoInheritance('PySignalSimulator',PySignalSimulator,PySignalSimulatorClass,DynamicDS,DynamicDSClass,ForceDevImpl=True) 171 | py.add_TgClass(PySignalSimulatorClass,PySignalSimulator,'PySignalSimulator') 172 | """ 173 | if any(type(p) is not tuple for p in ([device]+interfaces)): 174 | raise Exception,'TangoInterface_ArgumentIsNotTuple' 175 | device,deviceclass = device 176 | for interface,interfaceclass in interfaces: 177 | device,deviceclass = FullTangoInheritance(device.__name__, \ 178 | device,deviceclass, \ 179 | interface,interfaceclass, \ 180 | ForceDevImpl=True) 181 | return device,deviceclass 182 | 183 | def addCommandToTgClass(dev,dev_class,cmd_name,cmd_def,cmd_fun): 184 | """ 185 | @param cmd_def should be like [[argintype,"desc"],[argouttype,"desc"],{'property':value}] 186 | e.g: [[PyTango.DevString, "formula to evaluate"],[PyTango.DevString, "formula to evaluate"],{'Display level':PyTango.DispLevel.EXPERT,}] 187 | """ 188 | print 'Adding command %s to %s,%s device class' % (cmd_name,dev,dev_class) 189 | setattr(dev,cmd_name,cmd_fun) 190 | dev_class.cmd_list[cmd_name] = cmd_def 191 | return 192 | 193 | #def getAllBases(klass): 194 | #""" Gets recursively all super classes for a given class """ 195 | #bases = list(klass.__bases__) 196 | #for base in bases: 197 | #[bases.append(b) for b in getAllBases(base) if b not in bases] 198 | #return bases -------------------------------------------------------------------------------- /fandango/db.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ############################################################################# 4 | ## 5 | ## file : db.py 6 | ## 7 | ## description : This module simplifies database access. 8 | ## 9 | ## project : Tango Control System 10 | ## 11 | ## $Author: Sergi Rubio Manrique, srubio@cells.es $ 12 | ## 13 | ## $Revision: 2014 $ 14 | ## 15 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 16 | ## Bellaterra 17 | ## Spain 18 | ## 19 | ############################################################################# 20 | ## 21 | ## This file is part of Tango Control System 22 | ## 23 | ## Tango Control System is free software; you can redistribute it and/or 24 | ## modify it under the terms of the GNU General Public License as published 25 | ## by the Free Software Foundation; either version 3 of the License, or 26 | ## (at your option) any later version. 27 | ## 28 | ## Tango Control System is distributed in the hope that it will be useful, 29 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | ## GNU General Public License for more details. 32 | ## 33 | ## You should have received a copy of the GNU General Public License 34 | ## along with this program; if not, see . 35 | ########################################################################### 36 | 37 | """ 38 | This package implements a simplified acces to MySQL using FriendlyDB object. 39 | 40 | Go to http://mysql-python.sourceforge.net/MySQLdb.html for further information 41 | """ 42 | 43 | import time,datetime,log,traceback,sys 44 | import MySQLdb 45 | 46 | class FriendlyDB(log.Logger): 47 | """ 48 | Class for managing direct access to MySQL databases using mysql-python module 49 | """ 50 | def __init__(self,db_name,host='',user='',passwd='',autocommit=True,loglevel='WARNING',use_tuples=False,default_cursor=None): 51 | """ Initialization of MySQL connection """ 52 | self.call__init__(log.Logger,self.__class__.__name__,format='%(levelname)-8s %(asctime)s %(name)s: %(message)s') 53 | self.setLogLevel(loglevel or 'WARNING') 54 | #def __init__(self,api,db_name,user='',passwd='', host=''): 55 | #if not api or not database: 56 | #self.error('ArchivingAPI and database are required arguments for ArchivingDB initialization!') 57 | #return 58 | #self.api=api 59 | self.db_name=db_name 60 | self.host=host 61 | self.use_tuples = use_tuples #It will control if data is returned in tuples or lists 62 | self.setUser(user,passwd) 63 | self.autocommit = autocommit 64 | self.renewMySQLconnection() 65 | self.default_cursor = default_cursor or MySQLdb.cursors.Cursor 66 | self._cursor=None 67 | self._recursion = 0 68 | self.tables={} 69 | 70 | def __del__(self): 71 | if hasattr(self,'__cursor') and self._cursor: 72 | self._cursor.close() 73 | del self._cursor 74 | if hasattr(self,'db') and self.db: 75 | self.db.close() 76 | del self.db 77 | 78 | def setUser(self,user,passwd): 79 | """ Set User and Password to access MySQL """ 80 | self.user=user 81 | self.passwd=passwd 82 | 83 | def setAutocommit(self,autocommit): 84 | try: 85 | self.db.autocommit(autocommit) 86 | self.autocommit = autocommit 87 | except Exception,e: 88 | self.error('Unable to set MySQLdb.connection.autocommit to %s'%autocommit) 89 | raise Exception,e 90 | 91 | 92 | def renewMySQLconnection(self): 93 | try: 94 | if hasattr(self,'db') and self.db: 95 | self.db.close() 96 | del self.db 97 | self.db=MySQLdb.connect(db=self.db_name,host=self.host,user=self.user,passwd=self.passwd) 98 | self.db.autocommit(self.autocommit) 99 | except Exception,e: 100 | self.error( 'Unable to create a MySQLdb connection to "%s"@%s.%s: %s'%(self.user,self.host,self.db_name,str(e))) 101 | raise Exception,e 102 | 103 | def getCursor(self,renew=True,klass=None): 104 | ''' 105 | returns the Cursor for the database 106 | renew will force the creation of a new cursor object 107 | klass may be any of MySQLdb.cursors classes (e.g. DictCursor) 108 | MySQLdb.cursors.SSCursor allows to minimize mem usage in clients (although it relies memory cleanup to the server!) 109 | ''' 110 | try: 111 | if klass in ({},dict): 112 | klass = MySQLdb.cursors.DictCursor 113 | if (renew or klass) and self._cursor: 114 | if not self._recursion: 115 | self._cursor.close() 116 | del self._cursor 117 | if renew or klass or not self._cursor: 118 | self._cursor = self.db.cursor(self.default_cursor) if klass is None else self.db.cursor(cursorclass=klass) 119 | return self._cursor 120 | except: 121 | print traceback.format_exc() 122 | self.renewMySQLconnection() 123 | self._recursion += 1 124 | return self.getCursor(renew=True,klass=klass) 125 | 126 | def tuples2lists(self,tuples): 127 | ''' 128 | Converts a N-D tuple to a N-D list 129 | ''' 130 | return [self.tuples2lists(t) if type(t) is tuple else t for t in tuples] 131 | 132 | def table2dicts(self,keys,table): 133 | ''' Converts a 2-D table and a set of keys in a list of dictionaries ''' 134 | result = [] 135 | for line in table: 136 | d={} 137 | [d.__setitem__(keys[i],line[i]) for i in range(min([len(keys),len(line)]))] 138 | result.append(d) 139 | return result 140 | 141 | def fetchall(self,cursor=None): 142 | """ 143 | This method provides a custom replacement to cursor.fetchall() method. 144 | It is used to return a list instead of a big tuple; what seems to cause trouble to python garbage collector. 145 | """ 146 | vals = [] 147 | cursor = cursor or self.getCursor() 148 | while True: 149 | v = cursor.fetchone() 150 | if v is None: break 151 | vals.append(v) 152 | return vals 153 | 154 | def Query(self,query,export=True,asDict=False): 155 | ''' Executes a query directly in the database 156 | @param query SQL query to be executed 157 | @param export If it's True, it returns directly the values instead of a cursor 158 | @return the executed cursor, values can be retrieved by executing cursor.fetchall() 159 | ''' 160 | try: 161 | q=self.getCursor(klass = dict if asDict else self.default_cursor) 162 | q.execute(query) 163 | except: 164 | self.renewMySQLconnection() 165 | q=self.getCursor(klass = dict if asDict else None) 166 | q.execute(query) 167 | 168 | if not export: 169 | return q 170 | elif asDict or not self.use_tuples: 171 | return self.fetchall(q) #q.fetchall() 172 | else: 173 | return self.tuples2lists(self.fetchall(q)) #q.fetchall() 174 | 175 | def Select(self,what,tables,clause='',group='',order='',limit='',distinct=False,asDict=False,trace=False): 176 | ''' 177 | Allows to create and execute Select queries using Lists as arguments 178 | @return depending on param asDict it returns a list or lists or a list of dictionaries with results 179 | ''' 180 | if type(what) is list: what=','.join(what) if len(what)>1 else what[0] 181 | if type(tables) is list: tables=','.join(tables) if len(tables)>1 else tables[0] 182 | if type(clause) is list: 183 | clause=' and '.join('(%s)'%c for c in clause) if len(clause)>1 else clause[0] 184 | elif type(clause) is dict: 185 | clause1='' 186 | for i in range(len(clause)): 187 | k,v = clause.items()[i] 188 | clause1+= "%s like '%s'"%(k,v) if type(v) is str else '%s=%s'%(k,str(v)) 189 | if (i+1)1 else group[0] 193 | 194 | query = 'SELECT '+(distinct and ' DISTINCT ' or '') +' %s'%what 195 | if tables: query += ' FROM %s' % tables 196 | if clause: query += ' WHERE %s' % clause 197 | if group: group+= ' GROUP BY %s' % group 198 | if order: query += ' ORDER BY %s' % order 199 | if limit: query+= ' LIMIT %s' % limit 200 | 201 | result = self.Query(query,True,asDict=asDict) 202 | if not asDict and not self.use_tuples: 203 | return self.tuples2lists(result) 204 | else: 205 | return result 206 | #else: return self.table2dicts(what.split(',') if what!='*' else [k for t in tables.split(',') for k in self.getTableCols(t)],result) 207 | 208 | def getTables(self,load=False): 209 | ''' Initializes the keys of the tables dictionary and returns these keys. ''' 210 | if load or not self.tables: 211 | q=self.Query('show tables',False) 212 | [self.tables.__setitem__(t[0],[]) for t in self.tuples2lists(q.fetchall())] 213 | return sorted(self.tables.keys()) 214 | 215 | def getTableCols(self,table): 216 | ''' Returns the column names for the given table, and stores these values in the tables dict. ''' 217 | self.getTables() 218 | if not self.tables[table]: 219 | q=self.Query('describe %s'%table,False) 220 | [self.tables[table].append(t[0]) for t in self.tuples2lists(q.fetchall())] 221 | return self.tables[table] 222 | 223 | def getTableSize(self,table=''): 224 | table = table or '%'; 225 | res = self.Query("select table_name,table_rows from information_schema.tables where table_schema = '%s' and table_name like '%s';"%(self.db_name,table)) 226 | if not res: 227 | return 0 228 | elif len(res)==1: 229 | return res[0][1] 230 | else: 231 | return dict(res) 232 | 233 | def get_all_cols(self): 234 | if not self.tables: self.getTables() 235 | for t in self.tables: 236 | if not self.tables[t]: 237 | self.getTableCols(t) 238 | return 239 | -------------------------------------------------------------------------------- /fandango/interface/CopyCatDS.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | import PyTango 4 | import fandango 5 | import json 6 | 7 | from fandango.dynamic import * 8 | 9 | """ 10 | This module provides some method to extract command/attributes declaration of an existing device 11 | 12 | It will return dictionaries to emulate the copied class, and some methods to "bypass" its commands, record their behavior and later emulate it. 13 | 14 | Typical command declaration: 15 | 'CreateAlarmContext': 16 | [[PyTango.DevVarStringArray, "tag,attributes,..."], 17 | [PyTango.DevLong,"new context ID"]], 18 | 19 | 'CreateAlarmContext': 20 | [[PyTango.DevVarStringArray, "tag,attributes,..."], 21 | [PyTango.DevLong,"new context ID"]], 22 | 23 | Typical attributes declaration: 24 | 'VersionNumber': 25 | [[PyTango.DevString, 26 | PyTango.SCALAR, 27 | PyTango.READ], 28 | { 29 | 'Display level':PyTango.DispLevel.EXPERT, 30 | 'description':"Version number and release note", 31 | } ], 32 | 'PhoneBook': 33 | [[PyTango.DevString, 34 | PyTango.SPECTRUM, 35 | PyTango.READ, 512]], 36 | 'SentEmails': 37 | [[PyTango.DevString, 38 | PyTango.IMAGE, 39 | PyTango.READ, 512,512]], 40 | """ 41 | 42 | def get_cmd_descriptions(device): 43 | return dict((c.cmd_name,c) for c in fandango.get_device(device).command_list_query()) 44 | 45 | def copy_cmd_list(device): 46 | cmd_list = {} 47 | cmd_objs = get_cmd_descriptions(device) 48 | for c,obj in cmd_objs.items(): 49 | cmd_list[c] = [[obj.in_type,obj.in_type_desc],[obj.out_type,obj.out_type_desc]] 50 | return cmd_list 51 | 52 | def get_attr_descriptions(device): 53 | dp = fandango.get_device(device) 54 | aql = fandango.retry(dp.attribute_list_query) 55 | return dict((c.name,c) for c in aql) 56 | 57 | def copy_attr_list(device): 58 | attr_list = {} 59 | attr_objs = get_attr_descriptions(device) 60 | for a,obj in attr_objs.items(): 61 | attr_list[a] = [[PyTango.CmdArgType.values[obj.data_type],obj.data_format,obj.writable]] 62 | if obj.data_format == PyTango.SPECTRUM: attr_list[a][0].append(obj.max_dim_x) 63 | if obj.data_format == PyTango.IMAGE: attr_list[a][0].append(obj.max_dim_y) 64 | return attr_list 65 | 66 | def choose_db(url,default=None): 67 | if ':' not in url and default is not None: 68 | return default 69 | import os 70 | thost = os.getenv('TANGO_HOST') if ':' not in url else url.split('/')[0] 71 | print 'TANGO_HOST=%s'%thost 72 | return PyTango.Database(*thost.split(':')) 73 | 74 | class Doppelganger(fandango.SingletonMap): 75 | #Modes 76 | PLAYBACK,RECORD,BYPASS = 0,1,2 77 | 78 | # Device Properties 79 | device_property_list = { 80 | 'TargetDevice': 81 | [PyTango.DevString, 82 | "Device from which all attributes and commands will be mirrored", 83 | [ '' ] ], 84 | 'ReadOnly': 85 | [PyTango.DevBoolean, 86 | "If True, all attributes will be implemented as Read Only, and no Commands will be updated", 87 | [ False ] ], 88 | 'CopyAttributes': 89 | [PyTango.DevVarStringArray, 90 | "List of regular expressions to control which attributes will be copied", 91 | [ '*' ] ], 92 | 'CopyCommands': 93 | [PyTango.DevVarStringArray, 94 | "List of regular expressions to control which commands will be copied", 95 | [ '*' ] ], 96 | } 97 | 98 | def __init__(self,device,filename=''): 99 | self.proxy = fandango.get_device(device) 100 | self.commands = copy_cmd_list(self.proxy) 101 | self.attributes = copy_attr_list(self.proxy) 102 | self.filename = filename 103 | self.data = [] 104 | self.mode = self.BYPASS 105 | self.load() 106 | 107 | def load(self,filename=''): 108 | filename = filename or self.filename 109 | if not filename: return 110 | 111 | def __call__(self,argin=None): 112 | if self.mode in (self.BYPASS,self.RECORD): 113 | r = self.proxy.command_inout(*([self.command,argin] if argin is not None else [self.command])) 114 | if self.mode == self.RECORD: 115 | pass 116 | return r 117 | if self.mode == self.PLAYBACK: 118 | pass 119 | 120 | ############################################################################### 121 | 122 | class CopyCatDS(DynamicDS): 123 | def __init__(self,cl, name): 124 | _locals = locals() 125 | self.U = PyTango.Util.instance() 126 | self.ds_name = self.U.get_ds_name() 127 | self.db = self.U.get_database() 128 | #self.TargetDevice = fandango.tango.get_device_property(name,'TargetDevice') 129 | DynamicDS.__init__(self,cl,name,_locals=_locals,useDynStates=True) 130 | CopyCatDS.init_device(self) 131 | def delete_device(self): 132 | self.warning("[Device delete_device method] for device") 133 | def init_device(self): 134 | self.setLogLevel('DEBUG') 135 | self.info("In %s::init_device()"%self.get_name()) 136 | self.get_device_properties(self.get_device_class()) #Needed to do it twice to generate StaticAttributes properly 137 | fandango.tango.put_device_property(self.get_name(),dict((k,getattr(self,k)) for k in CopyCatDSClass.device_property_list),db=self.db) 138 | self.StaticAttributes = [] 139 | try: 140 | for a,t in sorted(copy_attr_list(self.TargetDevice).items()): 141 | if any(fandango.matchCl(e,a) for e in self.CopyAttributes): 142 | ttype,format,writable = t[0][:3] 143 | if not isTypeSupported(ttype): 144 | self.warning('%s attribute of type %s is not supported by DynamicDS'%(a,ttype.name)) 145 | else: 146 | dims = len(t[0][3:]) 147 | if dims==2: 148 | self.warning('%s attribute of type IMAGE is not supported by DynamicDS'%(a)) 149 | else: 150 | ttype = ttype.name if not dims else ttype.name.replace('Dev','DevVar')+'Array' 151 | if self.ReadOnly or writable==PyTango.AttrWriteType.READ: 152 | formula = "%s=%s(XATTR('%s'))"%(a,ttype,self.TargetDevice+'/'+a) 153 | else: 154 | formula = "%s=%s(XATTR('%s') if not WRITE else WATTR('%s',VALUE))"%(a,ttype,self.TargetDevice+'/'+a,self.TargetDevice+'/'+a) 155 | self.StaticAttributes.append(formula) 156 | self.info('Added %s'%formula) 157 | except: 158 | traceback.print_exc() 159 | self.set_status('Copied attributes:\n\t'+'\n\t'.join(self.StaticAttributes)) 160 | self.get_DynDS_properties() 161 | if self.DynamicStates: self.set_state(PyTango.DevState.UNKNOWN) 162 | self.info("Out of %s::init_device()"%self.get_name()) 163 | 164 | def always_executed_hook(self): 165 | DynamicDS.always_executed_hook(self) 166 | def read_attr_hardware(self,data): 167 | pass 168 | #def read_dyn_attr(self): 169 | 170 | #def write_dyn_attr(self): 171 | 172 | 173 | class CopyCatDSClass(DynamicDSClass): 174 | device_property_list = Doppelganger.device_property_list 175 | def __init__(self, name): 176 | PyTango.DeviceClass.__init__(self, name) 177 | self.set_type(name) 178 | def dyn_attr(self,dev_list): 179 | for dev in dev_list: 180 | CopyCatDS.dyn_attr(dev) 181 | 182 | class CopyCatServer(DynamicServer): 183 | """ 184 | The DynamicServer class provides .util .instance .db .classes to have access to Tango DS internals. 185 | """ 186 | def load_class(self,c): 187 | if c.endswith('_Copy'): return 188 | else: DynamicServer.load_class(self,c) 189 | 190 | def main(self,class_override=False): 191 | import fandango #needed to avoid startup exceptions when loading dynamically 192 | 193 | #fandango.tango.get_device_property failed! desc = BAD_INV_ORDER CORBA system exception: BAD_INV_ORDER_ORBHasShutdown 194 | #doppels = dict((d,(db.get_device_property(d,['TargetDevice'])['TargetDevice'] or [''])[0]) for d in self.classes['CopyCatDS']) 195 | ks = [k for k in self.classes if fandango.matchCl('CopyCatDS|(^*Copy$)',k)] 196 | print 'classes: %s'%ks 197 | doppels = sorted(set(t for k in ks for t in self.classes[k])) 198 | print 'copycat devices: %s'%doppels 199 | targets = dict((d,fandango.tango.get_device_property(d,'TargetDevice')) for d in doppels) 200 | classes = {} 201 | print 'targets: %s'%targets 202 | for t in set(targets.values()): 203 | if t: classes[t] = choose_db(t,self.db).get_class_for_device(t if ':' not in t else t.split('/',1)[-1]) 204 | print 'Devices: \n%s'%"\n".join(sorted('%s = %s(%s)'%(d,t,classes.get(t,None)) for d,t in targets.items())) 205 | if class_override and classes: 206 | for c in set(classes.values()): 207 | print('Adding %s_Copy ...'%c) 208 | import fandango.interface 209 | setattr(fandango.interface,'%s_Copy',CopyCatDS),setattr(fandango.interface,'%s_CopyClass',CopyCatDSClass) 210 | self.util.add_TgClass(CopyCatDSClass,CopyCatDS,c+'_Copy') 211 | for d in targets: 212 | fandango.tango.add_new_device(self.name,classes[targets[d]]+'_Copy',d) 213 | self.instance.server_init() 214 | self.instance.server_run() 215 | 216 | def CreateCommands(self,device,target): 217 | pass 218 | 219 | ############################################################################### 220 | 221 | def __test_Doppelganger(target = 'sys/tg_test/1'): 222 | dg = Doppelganger(target) 223 | print('%d commands'%len(dg.commands)) 224 | print(dg.commands.items()[0]) 225 | print('\n') 226 | print('%d attributes'%len(dg.attributes)) 227 | print(dg.attributes.items()[0]) 228 | 229 | def __test_CopyCatDS(target = 'sys/tg_test/1'): 230 | fandango.tango.add_new_device('CopyCatDS/test','CopyCatDS','test/copycatds/01') 231 | PyTango.Database().put_device_property('test/copycatds/01',{'TargetDevice':[target]}) 232 | ds = CopyCatServer('CopyCatDS/test',log='-v4') 233 | ds.main() 234 | 235 | if __name__ == '__main__': 236 | import sys 237 | if '--test' in sys.argv: 238 | __test_Doppelganger('sys/tg_test/1') 239 | print '\n' 240 | __test_CopyCatDS('sys/tg_test/1',class_override=True) 241 | else: 242 | ds = CopyCatServer('CopyCatDS/'+sys.argv[1],log=(sys.argv[2:] or ['-v2'])[0]) 243 | ds.main() 244 | -------------------------------------------------------------------------------- /fandango/excepts.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | #!/usr/bin/env python2.5 3 | """ 4 | ############################################################################# 5 | ## 6 | ## file : excepts.py 7 | ## 8 | ## description : see below 9 | ## 10 | ## project : Tango Control System 11 | ## 12 | ## $Author: Sergi Rubio Manrique, srubio@cells.es $ 13 | ## 14 | ## $Revision: 2008 $ 15 | ## 16 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 17 | ## Bellaterra 18 | ## Spain 19 | ## 20 | ############################################################################# 21 | ## 22 | ## This file is part of Tango Control System 23 | ## 24 | ## Tango Control System is free software; you can redistribute it and/or 25 | ## modify it under the terms of the GNU General Public License as published 26 | ## by the Free Software Foundation; either version 3 of the License, or 27 | ## (at your option) any later version. 28 | ## 29 | ## Tango Control System is distributed in the hope that it will be useful, 30 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | ## GNU General Public License for more details. 33 | ## 34 | ## You should have received a copy of the GNU General Public License 35 | ## along with this program; if not, see . 36 | ########################################################################### 37 | 38 | This module excepts provides two ways of simplifying exceptions logging. 39 | 40 | ExceptionWrapper is a decorator that provides the @Catched keyword to be added before functions declarations. 41 | @Catched 42 | def fun(*args): 43 | pass 44 | 45 | ExceptionManager is a Contextmanager object that can be used in a with statement: 46 | with ExceptionManager(): 47 | fun(args) 48 | 49 | The usage of the parameter of the tango throw_exception() method: 50 | This method have 3 parameters which are called (all of them are strings) 51 | 52 | - Reason, one word like _ 53 | - Desc 54 | - Origin 55 | 56 | Example: 57 | PyTango.Except.throw_exception("PyPLC_ModbusDevNotDef","Modbus Device not defined",inspect.currentframe().f_code.co_name) 58 | 59 | 60 | ADVICE: PyTango.re_throw_exception it's failing a lot, try to use that instead: 61 | 62 | except PyTango.DevFailed, e: 63 | print e 64 | #PyTango.Except.re_throw_exception(e,"DevFailed Exception",str(e),inspect.currentframe().f_code.co_name) 65 | PyTango.Except.throw_exception(str(e.args[0]['reason']),str(e.args[0]['desc']),inspect.currentframe().f_code.co_name+':'+str(e.args[0]['origin'])) 66 | cheers ... 67 | 68 | 69 | # Get the current frame, the code object for that frame and the name of its object 70 | import inspect 71 | print inspect.currentframe().f_code.co_name 72 | """ 73 | 74 | import sys 75 | import traceback 76 | import functools 77 | import contextlib 78 | from fandango import log 79 | from objects import decorator_with_args 80 | import fandango.functional as fun 81 | 82 | try: 83 | from PyTango import DevFailed,Except 84 | except: 85 | DevFailed,Except = Exception,Exception 86 | 87 | def trial(tries,excepts=None,args=None,kwargs=None,return_exception=None): 88 | """ This method executes a try,except clause in a single line 89 | :param tries: may be a callable or a list of callables 90 | :param excepts: it can be a callable, a list of callables, a map of {ExceptionType:[callables]} or just a default value to return 91 | :param args,kwargs: arguments to be passed to the callable 92 | :return exception: whether to return exception or None (default) 93 | """ 94 | try: 95 | return_exception = return_exception or (excepts is not None and not any(fun.isCallable(x) for x in fun.toSequence(excepts))) 96 | tries = fun.toSequence(tries) 97 | args = fun.toSequence(fun.notNone(args,[])) 98 | kwargs = fun.notNone(kwargs,{}) 99 | excepts = fun.notNone(excepts,lambda e: log.printf(str(e))) 100 | result = [t(*args,**kwargs) for t in tries if fun.isCallable(t)] 101 | return result[0] if len(result)==1 else result 102 | except Exception,e: 103 | if fun.isCallable(excepts): 104 | v = excepts(e) 105 | return v if return_exception else None 106 | else: 107 | if fun.isDictionary(excepts): 108 | if type(e) in excepts: excepts = excepts.get(type(e)) 109 | elif type(e).__name__ in excepts: excepts = excepts.get(type(e).__name__) 110 | else: 111 | candidates = [t for t in excepts if isinstance(e,t)] 112 | if candidates: excepts = excepts[candidates[0]] 113 | else: excepts = excepts.get('') or excepts.get(None) or [] 114 | vals = [x(e) for x in fun.toSequence(excepts) if fun.isCallable(x)] 115 | if return_exception: 116 | return vals or fun.notNone(excepts,None) 117 | 118 | exLogger = log.Logger() 119 | 120 | def getLastException(): 121 | """ returns last exception traceback """ 122 | return str(traceback.format_exc()) 123 | 124 | """ 125 | @TODO: These methods failed with python 2.6; to be checked ... 126 | 127 | def get_exception_line(as_str=False): 128 | ty,e,tb = sys.exc_info() 129 | file,line = tb[-1][:2] if tb else ('',0) 130 | result = (file,line,tb) 131 | if as_str: return '%s[%d]: %s!'%result 132 | else: return result 133 | 134 | def exc2str(e): 135 | if isinstance(e,DevFailed): 136 | msg='' 137 | try: 138 | msg = getattr(e.args[0],'description',e.args[0]['description']) 139 | except: 140 | msg = [s for s in str(e).split('\n') if 'desc' in s][0].strip() 141 | return 'DevFailed(%s)'%msg 142 | else: 143 | return get_exception_line(as_str=True) 144 | """ 145 | 146 | def getPreviousExceptions(limit=0): 147 | """ 148 | sys.exc_info() returns : type,value,traceback 149 | traceback.extract_tb(traceback) : returns (filename, line number, function name, text) 150 | """ 151 | try: 152 | exinfo = sys.exc_info() 153 | if exinfo[0] is not None: 154 | stack = traceback.format_tb(exinfo[2]) 155 | return str('\n'.join(['Tracebacks (most recent call last):', 156 | ''.join(stack[(len(stack)>1 and 1 or 0):]), 157 | ': '.join([str(exinfo[0].__name__),str(exinfo[1])]) 158 | ])) 159 | else: 160 | return '' 161 | except Exception,e: 162 | print 'Aaaargh!' 163 | return traceback.format_exc() 164 | 165 | class RethrownException(Exception): 166 | pass 167 | 168 | 169 | #@decorator_with_args #This decorator disturbed stdout!!!! ... There's a problem calling nested decorators!!! 170 | def ExceptionWrapper(fun,logger=exLogger,postmethod=None, showArgs=False,verbose=False,rethrow=False,default=None): 171 | ''' 172 | Implementation of the popular Catched() decorator: 173 | 174 | * it will execute your method within a a try/except 175 | * it will print the traceback 176 | * if :rethrow: is False it will return :default: in case of exception 177 | 178 | Example: 179 | @ExceptionWrapper 180 | def funny(): 181 | print 'what?' 182 | end 183 | 184 | funny() 185 | ''' 186 | def wrapper(*args,**kwargs): 187 | try: 188 | #logger.debug('Trying %s'%fun.__name__) 189 | result = fun(*args,**kwargs) 190 | #sys.stdout.write('fun DONE!!!\n') 191 | return result 192 | except DevFailed, e: 193 | exstring=getPreviousExceptions() 194 | if verbose: 195 | print '-'*80 196 | #exstring = traceback.format_exc() 197 | logger.warning('DevFailed Exception Catched: \n%s'%exstring) 198 | try: 199 | if showArgs: logger.info('%s(*args=%s, **kwargs=%s)'%(fun.__name__,args,kwargs)) 200 | except:pass 201 | sys.stdout.flush(); sys.stderr.flush() 202 | print '-'*80 203 | 204 | if postmethod: postmethod(exstring) 205 | err = e.args[0] 206 | if rethrow: 207 | #Except.re_throw_exception(e,'','',"%s(...)"%fun.__name__) 208 | Except.throw_exception(err.reason, exstring, "%s(...)"%fun.__name__) 209 | else: 210 | #Except.throw_exception(err.reason,err.desc,err.origin) 211 | logger.warning(str((err.reason,err.desc,err.origin))) 212 | return default 213 | except Exception,e: 214 | #exstring = traceback.format_exc() 215 | exstring=getPreviousExceptions() 216 | if verbose: 217 | print '-'*80 218 | logger.error('Python Exception catched: \n%s'%exstring) 219 | try: 220 | if showArgs: logger.info('%s(*args=%s, **kwargs=%s)'%(fun.__name__,args,kwargs)) 221 | except:pass 222 | print '-'*80 223 | if postmethod: postmethod(exstring) 224 | if rethrow: 225 | Except.throw_exception('Exception',exstring,"%s(...)"%fun.__name__) 226 | else: 227 | return default 228 | pass 229 | 230 | #ExceptionWrapper behaves like a decorator 231 | functools.update_wrapper(wrapper,fun) #it copies all function information to the wrapper 232 | return wrapper 233 | 234 | Catched = ExceptionWrapper 235 | CatchedArgs = decorator_with_args(ExceptionWrapper) 236 | 237 | class ExceptionManager(object): 238 | """ 239 | This was a version of ExceptionWrapper to be used as ContextManager together with *with* statement. 240 | Not really tested nor used, just a proof of concept. 241 | """ 242 | def __init__(self,logger=exLogger,origin=None,postmethod=None,verbose=True,rethrow=True): 243 | self.logger=logger 244 | self.postmethod=postmethod 245 | self.verbose=verbose 246 | self.rethrow=rethrow 247 | self.origin=origin 248 | pass 249 | 250 | def __enter__(self): 251 | pass 252 | 253 | #@Catched 254 | def __exit__(self,etype,e,tb): #Type of exception, exception instance, traceback 255 | if not e and not etype: 256 | pass 257 | else: 258 | stack = traceback.format_tb(tb) 259 | exstring = '\n'.join(stack) 260 | if self.verbose: 261 | print '-'*80 262 | self.logger.warning('%s Exception Catched, Tracebacks (most recent call last): %s;\n%s'%(etype.__name__,str(e),exstring)) 263 | sys.stdout.flush(); sys.stderr.flush() 264 | print '-'*80 265 | 266 | if self.postmethod: self.postmethod(exstring) 267 | if etype is DevFailed: 268 | #for k,v in e[0].items():print k,':',v 269 | if True: #not self.rethrow: #re_throw doesn't work! 270 | #The exception is throw just as it was 271 | err = e[0] 272 | Except.throw_exception(err.reason,err.desc,err.origin) 273 | #Except.throw_exception(e.args[0]['reason'],e.args[0]['desc'],e.args[0]['origin']) 274 | else: #It doesn't work!!! 275 | #ex=DevFailed(e[0]['reason'],e[0]['desc'],e[0]['origin']) 276 | #Except.re_throw_exception(ex, '','','') 277 | pass 278 | else: #elif etype is Exception: 279 | exstring = self.origin or len(exstring)<125 and exstring or stack[-1] 280 | Except.throw_exception(etype.__name__,str(e),exstring) 281 | 282 | -------------------------------------------------------------------------------- /fandango/devslist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | """ 3 | ############################################################################# 4 | ## 5 | ## file : devslist.py 6 | ## 7 | ## description : see below 8 | ## 9 | ## project : Tango Control System 10 | ## 11 | ## $Author: Sergi Rubio Manrique, srubio@cells.es $ 12 | ## 13 | ## $Revision: 2008 $ 14 | ## 15 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 16 | ## Bellaterra 17 | ## Spain 18 | ## 19 | ############################################################################# 20 | ## 21 | ## This file is part of Tango Control System 22 | ## 23 | ## Tango Control System is free software; you can redistribute it and/or 24 | ## modify it under the terms of the GNU General Public License as published 25 | ## by the Free Software Foundation; either version 3 of the License, or 26 | ## (at your option) any later version. 27 | ## 28 | ## Tango Control System is distributed in the hope that it will be useful, 29 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | ## GNU General Public License for more details. 32 | ## 33 | ## You should have received a copy of the GNU General Public License 34 | ## along with this program; if not, see . 35 | ########################################################################### 36 | 37 | There's two things that must be done in this module: 38 | 39 | Keep a list with all the devices within a device server, or used by a device server 40 | Objects will be instances of Device3_Impl or DeviceProxy 41 | 42 | The AttributeProxy must have a list of all attributes, and the subscribers for them! 43 | And attribute must be checked before adding it to the list. 44 | Firing of events must be thread-safe (threading.Event, threading.Lock) 45 | """ 46 | """ 47 | #--------------------------------------------------------------------------- 48 | # THIS CLASS IS A DUPLICATION OF WHAT IS DONE IN CALLBACKS? 49 | # Not if it is use to manage the duality Instance/DProxy for devices that run in the same server or others 50 | # It should replace the calls to DeviceProxy done inside device.DevChild or PyStateComposer 51 | #--------------------------------------------------------------------------- 52 | 53 | To this class should be added all the devices in a device server, and all the devices that are referenced from them. 54 | It includes all the Parent devices for those abstract devices running in the server. 55 | It will centralize the DeviceProxy access, and will avoid its use when internal communication is possible. 56 | """ 57 | 58 | import PyTango 59 | import threading 60 | import objects 61 | 62 | #These objects will be used by DeviceServer to create PyTango.PyUtil and PyTango.Util and keep servers data 63 | #py = None 64 | #U = None 65 | 66 | class DeviceList(dict): 67 | ''' 68 | DevicesList is a dictionary that provides some additional elements to be used with Tango Devices: 69 | The dictionary can be used to link a device name to any type of object, but additional methods are used to manage DeviceProxies 70 | isProxy(name): returns True if the object is a DeviceProxy 71 | subscribe(name,_event): each device has a list of 'subscribers', this method allows to add a new object to the list 72 | 73 | __subscribers will keep a list of methods to be called each time that a device receives an event 74 | 75 | * __len__( self) 76 | * __getitem__( self, key) 77 | * __setitem__( self, key, value) 78 | * __delitem__( self, key) 79 | * __iter__( self) 80 | * __contains__( self, item) 81 | 82 | ''' 83 | MAX_ERRORS=10.0 84 | MIN_PERIOD=3.0 #This is the min period between read_attribute executions 85 | TIME_WAIT=15.0 #Time to wait between check_proxies execution 86 | 87 | def __init__(self,_dict={}): 88 | dict.__init__(self,_dict) 89 | self.__db = PyTango.Database() 90 | self.state=PyTango.DevState.INIT 91 | 92 | self.dp_event=threading.Event() 93 | self.dp_stopEvent=threading.Event() 94 | self.dp_lock=threading.Lock() 95 | 96 | #Add this line to always_executed_hook: 97 | # if hasattr(self,'ForceParentPolling') and self.ForceParentPolling and not self.ParentPolledAttributes: self.force_ParentAttributes_polling() 98 | #self.dp_force_polling=ForcePolling 99 | #self.ParentPolledAttributes=[] 100 | 101 | #self.MAX_ERRORS=MAX_ERRORS 102 | #self.MIN_PERIOD=3.0 103 | 104 | self.dp_errors = 0 105 | 106 | self.check_dp_thread=threading.Thread(None,self.check_proxies,'check_proxies') 107 | self.check_dp_thread.setDaemon(False) 108 | 109 | #The Thread must be started once all the listeners have been initialized ... in always_executed_hook? 110 | #if not self.check_dp_thread.isAlive(): 111 | #self.check_dp_thread.start() 112 | 113 | pass 114 | 115 | def __setitem__(self,key,value): 116 | dict.__setitem__(self,key,innerDevice(value)) 117 | 118 | def append(self,value): 119 | if isinstance(value,PyTango.DeviceImpl): key=value.get_name() 120 | elif isinstance(value,PyTango.DeviceProxy): key=value.name() 121 | elif isinstance(value,str): key=value 122 | self.__setitem__(key,value) 123 | def add(self,value): self.append(value) 124 | 125 | def init_proxy(self,key,value): 126 | self.__setitem__(key,value) 127 | 128 | def get_state(self): return self.state 129 | def set_state(self,_state): self.state=_state 130 | 131 | 132 | def isAlive(self): return self.check_dp_thread.isAlive() 133 | def start(self): 134 | if not self.isAlive(): 135 | self.check_dp_thread.start() 136 | 137 | def check_proxies(self): 138 | ''' This method performs the parent's device proxy checking 139 | It changes the state to Unknown if there's no connection to the device 140 | The state is set to INIT when the connection is restablished 141 | stop_threads and dp_event help to control the deletion of the thread 142 | ''' 143 | #signal.signal(signal.SIGTRAP,self.delete_device) 144 | #signal.signal(signal.SIGABRT,self.delete_device) 145 | #signal.signal(signal.SIGKILL,self.delete_device) 146 | while not self.dp_stopEvent.isSet(): 147 | for dname,idev in self.iteritems(): 148 | if self.dp_stopEvent.isSet(): break 149 | print '*'*80 150 | print 'in DevsList.check_proxies, ...' 151 | print '*'*80 152 | 153 | self.dp_lock.acquire() 154 | if self[dname].State==PyTango.DevState.UNKNOWN: 155 | print 'in DevsList.check_proxies, [%s] is UNKNOWN'%dname 156 | try: 157 | idev.dp = PyTango.DeviceProxy(dname) 158 | idev.dp.set_timeout_millis(1000) 159 | idev.State=PyTango.DevState.INIT 160 | idev.dp.ping() 161 | #idev.check_dp_attributes() 162 | idev.dp_errors=0 163 | idev.last_time=time.time() 164 | print "In DevsList.check_proxies: proxy to "+self.ParentName+" device initialized." 165 | except Exception,e: 166 | print 'EXCEPTION in DevsList.check_proxies:, %s Proxy Initialization failed!'%dname,getLastException() 167 | idev.State=PyTango.DevState.UNKNOWN 168 | idev.dp_errors+=1 169 | #del self.dp; self.dp=None 170 | else: 171 | try: 172 | idev.dp.ping() 173 | #idev.check_dp_attributes_epoch() 174 | except Exception,e: 175 | print 'EXCEPTION in DevsList.check_proxies, Ping to device ',self.ParentName,' failed!: ',getLastException() 176 | idev.State=PyTango.DevState.UNKNOWN 177 | self.dp_errors+=1 178 | #del self.dp; self.dp=None 179 | 180 | if all([v.State==UNKNOWN for v in self.values()]): 181 | self.set_state(PyTango.DevState.UNKNOWN) 182 | elif idev.State==PyTango.DevState.INIT: 183 | self.set_state(PyTango.DevState.INIT) 184 | elif idev.State!=PyTango.DevState.UNKNOWN: 185 | self.set_state(PyTango.DevState.RUNNING) 186 | 187 | self.dp_lock.release() 188 | self.dp_event.wait(self.MIN_PERIOD) 189 | 190 | self.dp_event.wait(self.TIME_WAIT) 191 | 192 | #if self.dp: del self.dp 193 | pass 194 | 195 | #ListOfDevices=DevicesList() 196 | DeviceList=DeviceList() 197 | 198 | class innerDevice(objects.Object): 199 | def __init__(self,device): 200 | if isinstance(device,PyTango.DeviceProxy): 201 | self.dp=proxy 202 | elif isinstance(device,PyTango.DeviceImpl): 203 | if hasattr(device,'myProxy'): 204 | self.dp=device.myProxy 205 | else: 206 | self.dp=PyTango.DeviceProxy(device.get_name()) 207 | device.myProxy=self.dp 208 | elif isinstance(device,str): 209 | self.dp=PyTango.DeviceProxy(device) 210 | else: 211 | raise 'innerDevice_UnknownArgumentType' 212 | 213 | self.State=PyTango.DevState.UNKNOWN 214 | self.name=self.dp.name() 215 | self.admin=self.dp.adm_name() 216 | self.subscribers=[] #This will be a list of objects, each being a device, a threading.Event or a method to be called 217 | self.last_comm='' 218 | self.last_time=0. #Last succesful communication 219 | self.last_retry=0. #Last communication retry 220 | self.last_update={} #Dictionary with the last update time for each attribute 221 | self.dp_errors=0 222 | self.attributeList=[] 223 | pass 224 | def isProxy(self): 225 | return isinstance(self.dp,PyTango.DeviceProxy) 226 | def push_event(self,_event): 227 | for rec in self.subscribers: 228 | if hasattr(rec,'push_event'): rec.push_event(_event) 229 | elif isinstance(rec,threading.Event): rec.set() 230 | elif callable(rec): rec() 231 | pass 232 | 233 | #class innerDevProxy(objects.Object): 234 | #""" This class will be used to simulate a PyTango.DeviceProxy for those devices running within the same device server """ 235 | #def __init__(self,DI=None): 236 | #self.DevImpl=DI 237 | #pass 238 | #def __del__(self): 239 | #pass 240 | #def ping(self): 241 | #return True 242 | #pass 243 | #def state(self): return self.DevImpl.get_state() 244 | #def get_name(self): return self.DevImpl.get_name() 245 | 246 | #def command_inout(self,comm,argin): return getattr(self.DevImpl,comm)(argin) 247 | 248 | #def read_attribute(self,aname): 249 | ##attlist=self.get_attribute_list() 250 | #multiattr=self.get_device_attr() 251 | #attr=multiattr.get_attr_by_name(aname) 252 | #if isinstance(self.DevImpl,dynamic.DynamicDS) and aname in self.DevImpl.get_dyn_attr_list():: 253 | #return self.DevImpl.read_dyn_attr(attr) 254 | #else: return getattr(self.DevImpl,'read_'+attr.get_name())(attr) 255 | #def write_attribute(self,attribute,attvalue): 256 | #raise 'innerDevProxyException_READONLY' 257 | 258 | #def subscribe_event(self): 259 | #pass 260 | #def get_attribute_list(self,All=True): 261 | #attr_list = [] 262 | #if All: attr_list = self.DevImpl.get_device_class().attr_list.keys() 263 | #if isinstance(self.DevImpl,dynamic.DynamicDS): 264 | #attr_list = attr_list + self.DevImpl.get_dyn_attr_list() 265 | #pass 266 | #pass 267 | 268 | #class AttributesList(dict): 269 | #def addAttribute(self,dev): 270 | #''' It returns a threading.Event that will be set each''' 271 | #event=threading.Event() 272 | #return event 273 | #pass 274 | #def updateAttribute(self,dev): 275 | 276 | #pass 277 | #pass -------------------------------------------------------------------------------- /fandango/web.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | """ 3 | ############################################################################# 4 | ## 5 | ## file : web.py 6 | ## 7 | ## description : see below 8 | ## 9 | ## project : Tango Control System 10 | ## 11 | ## $Author: Sergi Rubio Manrique, srubio@cells.es $ 12 | ## 13 | ## $Revision: 2010 $ 14 | ## 15 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 16 | ## Bellaterra 17 | ## Spain 18 | ## 19 | ############################################################################# 20 | ## 21 | ## This file is part of Tango Control System 22 | ## 23 | ## Tango Control System is free software; you can redistribute it and/or 24 | ## modify it under the terms of the GNU General Public License as published 25 | ## by the Free Software Foundation; either version 3 of the License, or 26 | ## (at your option) any later version. 27 | ## 28 | ## Tango Control System is distributed in the hope that it will be useful, 29 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | ## GNU General Public License for more details. 32 | ## 33 | ## You should have received a copy of the GNU General Public License 34 | ## along with this program; if not, see . 35 | ########################################################################### 36 | @package dicts 37 | 38 | Some extensions to python dictionary 39 | ThreadDict: Thread safe dictionary with redefinable read/write methods and a backgroud thread for hardware update. 40 | defaultdict_fromkey: Creates a dictionary with a default_factory function that creates new elements using key as argument. 41 | CaselessDict: caseless dictionary 42 | CaselessDefaultDict: a join venture between caseless and default dict 43 | 44 | @deprecated 45 | @note see in tau.core.utils.containers 46 | 47 | by Sergi Rubio, 48 | srubio@cells.es, 49 | 2010 50 | """ 51 | 52 | 53 | import sys,time,traceback 54 | import fandango 55 | from fandango.functional import time2str,isString,isSequence,toList 56 | import pickle 57 | import PyQt4 58 | from PyQt4 import Qt 59 | from PyQt4.Qt import QColor 60 | #import fandango.qt 61 | 62 | #import guiqwt 63 | #import guidata 64 | #from guiqwt.plot import CurveDialog 65 | #from guiqwt.builder import make 66 | #from guiqwt.styles import COLORS 67 | 68 | ############################################################################### 69 | 70 | page = lambda s: '%s'%s 71 | body = lambda s: '%s'%s 72 | paragraph = lambda s: '

%s

'%s 73 | code = lambda s: '
%s
'%str(s) 74 | raw = code 75 | linebreak = '
' 76 | separator = '
' 77 | 78 | ulist = lambda s: '
    %s
'%s 79 | item = lambda s: '
  • %s
  • '%s 80 | 81 | bold = lambda s: '%s'%s 82 | em = lambda s: ''+str(s)+'' 83 | under = lambda s: '\n'+str(s)+'\n' 84 | camel = lambda s: ''.join(r[0].upper()+(r[1:] or '').lower() for r in s.split()) 85 | color = lambda s,color: '%s'%(camel(color),s) 86 | colors = "black white yellow blue green red gray fuchsia lime maroon aqua navy olive purple silver teal".split() 87 | size = lambda s,size: '%s'%(size,s) 88 | 89 | link = lambda s,url: '%s' % (url,s) 90 | iname = lambda s: s.replace(' ','').lower() 91 | iurl = lambda url: '#'+iname(url) 92 | ilink = lambda s,url: '%s' % (iname(s),s) 93 | title = lambda s,n=1: '%s' % (iname(s),n,s,n) # allows to make titles linkable 94 | title1 = lambda s: '

    %s

    '%s 95 | 96 | row,cell = (lambda s: '%s'%s) , (lambda s: '%s'%s) 97 | table = lambda s: '\n'+'\n'.join([row(''.join([ 98 | (cell('%s'%c) if (len(s)==1 or j or (len(toList(r))<3 and i)) else cell(bold('%s'%c))) 99 | for j,c in enumerate(toList(r))])) 100 | for i,r in enumerate(s)] 101 | )+'\n
    ' 102 | 103 | def list_to_ulist(l): 104 | return ulist('\n'.join(item(str(s)) for s in l)) 105 | 106 | def dicts2table(dcts,keys=None,formatter=None): 107 | """ 108 | :param dcts: a list of dictionaries 109 | :param keys: an alternative list of keys 110 | """ 111 | if not keys: keys = sorted(set(k for k in dct.keys() for dct in dcts)) 112 | if not formatter: formatter = lambda s:s 113 | lines = [keys,] 114 | for dct in dcts: 115 | lines.append([formatter(dct.get(k,'')) for k in keys]) 116 | return table(lines) 117 | 118 | def dict2dict2table(seq,keys=None,formatter=None): 119 | """ 120 | :param seq: a nested dictionary {:{}} 121 | :param keys: a list of header names 122 | """ 123 | if not keys: keys = [""]+sorted(set(k for v in seq.values() for k in v.keys())) 124 | if not formatter: formatter = lambda s:s 125 | lines = [keys,] 126 | for k,v in sorted(seq.items()): 127 | line = [k] 128 | for k in keys[1:]: 129 | line.append(formatter(v.get(k,''))) 130 | lines.append(line) 131 | return table(lines) 132 | 133 | 134 | ############################################################################### 135 | 136 | MULTIPLIER = 10 137 | OFFSETS = [-15,-30,-45,-60,-75,-90,-105,-120] 138 | fg,bg = QColor('red'),QColor('blue') 139 | FECOLORS = [ 140 | QColor('grey'), 141 | QColor('brown'), 142 | QColor('darkgreen'), 143 | QColor('violet'), 144 | QColor('lightblue'), 145 | QColor('lightgreen'), 146 | QColor('orange'), 147 | QColor('magenta'), 148 | ] 149 | 150 | #ats = [CURRENT]+FRONTENDS 151 | #all_colors = [fg]+FECOLORS 152 | #t = [time.time()-DAYS*24*3600,time.time()] 153 | 154 | ############################################################################### 155 | #import PyTangoArchiving 156 | #from PyTangoArchiving.utils import decimate_array 157 | ############################################################################### 158 | 159 | def jqplot(title,ats): 160 | #USING jqPlot instead of Qt 161 | js = 'http://www.cells.es/static/Files/Computing/Controls/Reports/js' 162 | includes = """ 163 | 164 | 165 | 166 | 167 | 168 | """.replace('$JS',js) 169 | jqp_template = """ 170 |
    171 | 222 | """ 223 | serie = """ 224 | { 225 | label:'$ATTR', 226 | lineWidth: 3, 227 | //color:'#5FAB78', 228 | color: "$COLOR", 229 | showMarker:false, 230 | //fill:true, 231 | //fillAndStroke:true, 232 | } 233 | """#.replace('$ATTR',CURRENT).replace('$COLOR','rgba(255, 0, 0, 0.5)') 234 | series = 'series:[\n%s\n]'%',\n'.join([ 235 | serie.replace('$ATTR',a).replace('$COLOR','rgba(%d,%d,%d,1)'%all_colors[i].getRgb()[:3]) 236 | for i,a in enumerate(ats) 237 | ]) 238 | #data = """[[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]""" 239 | data = str([ 240 | list([fandango.time2str(t[0]),t[1]] for t in vals[k]) for k in ats] 241 | ) 242 | return jqp_template.replace('$DATA',data).replace('$SERIES',series).replace('$TITLE',title) -------------------------------------------------------------------------------- /fandango/log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | """ 3 | ############################################################################# 4 | ## 5 | ## file : log.py 6 | ## 7 | ## description : see below 8 | ## 9 | ## project : Tango Control System 10 | ## 11 | ## $Author: tcoutinho@cells.es, extended by srubio@cells.es $ 12 | ## 13 | ## 14 | ## $Revision: 2008 $ 15 | ## 16 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 17 | ## Bellaterra 18 | ## Spain 19 | ## 20 | ############################################################################# 21 | ## 22 | ## This file is part of Tango Control System 23 | ## 24 | ## Tango Control System is free software; you can redistribute it and/or 25 | ## modify it under the terms of the GNU General Public License as published 26 | ## by the Free Software Foundation; either version 3 of the License, or 27 | ## (at your option) any later version. 28 | ## 29 | ## Tango Control System is distributed in the hope that it will be useful, 30 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | ## GNU General Public License for more details. 33 | ## 34 | ## You should have received a copy of the GNU General Public License 35 | ## along with this program; if not, see . 36 | ########################################################################### 37 | 38 | Example of usage: 39 | class logged_class(Logger): 40 | def __init__(self,name,system): 41 | #parent must be also an instance of a Logger object 42 | self.call__init__(Logger,name,parent=system) 43 | pass 44 | ... 45 | 46 | Example of logging: 47 | In [17]: import logging 48 | In [18]: l = logging.getLogger("something") 49 | In [19]: l.debug("message") 50 | In [20]: l.error("message") 51 | No handlers could be found for logger "something" 52 | In [21]: l.addHandler(logging.StreamHandler()) 53 | In [22]: l.error("message") 54 | message 55 | 56 | """ 57 | 58 | import time, logging, weakref, traceback 59 | from objects import Object 60 | from pprint import pprint 61 | from functional import time2str 62 | import warnings 63 | 64 | 65 | def printf(*args): 66 | # This is a 'lambdable' version of print 67 | print(''.join(map(str,args))) 68 | 69 | def shortstr(s,max_len=144): 70 | s = str(s) 71 | if max_len>0 and len(s) > max_len: 72 | s = s[:max_len-3]+'...' 73 | return s 74 | 75 | def except2str(e=None,max_len=int(7.5*80)): 76 | if e is None: e = traceback.format_exc() 77 | e = str(e) 78 | if 'desc=' in e or 'desc =' in e: 79 | r,c = '',0 80 | for i in range(e.count('desc')): 81 | c = e.index('desc',c)+1 82 | r+=e[c-15:c+max_len-18]+'...\n' 83 | result = r 84 | else: 85 | result = str(e)[-(max_len-3):]+'...' 86 | return result or e[:max_len] 87 | 88 | class FakeLogger(): 89 | """ 90 | This class just simulates a Logger using prints with date and header, it doesn't allow any customization 91 | """ 92 | _instances = [] 93 | def __init__(self,header='',keep=False): 94 | self.header = '%s: '%header if header else '' 95 | if keep: self._instances.append(self) 96 | def trace(self,s):print time2str()+' '+'TRACE\t'+self.header+s 97 | def debug(self,s):print time2str()+' '+'DEBUG\t'+self.header+s 98 | def info(self,s):print time2str()+' '+'INFO\t'+self.header+s 99 | def warning(self,s):print time2str()+' '+'WARNING\t'+self.header+s 100 | def error(self,s):print time2str()+' '+'ERROR\t'+self.header+s 101 | 102 | class Logger(Object): 103 | """ 104 | This class provides logging methods (debug,info,error,warning) to all classes inheriting it. 105 | To use it you must inherit from it and add it within your __init__ method: 106 | 107 | def __init__(self,cl, name): 108 | PyTango.Device_4Impl.__init__(self,cl,name) 109 | self.call__init__(fandango.log.Logger,name,format='%(levelname)-8s %(asctime)s %(name)s: %(message)s') 110 | 111 | Constructor arguments allow to customize the output format: 112 | * name='fandango.Logger' #object name to appear at the beginning 113 | * parent=None 114 | * format='%(levelname)-8s %(asctime)s %(name)s: %(message)s'\ 115 | * use_tango=True #Use Tango Logger if available 116 | * use_print=True #Use printouts instead of linux logger (use_tango will override this option) 117 | * level='INFO' #default log level 118 | * max_len=0 #max length of log strings 119 | """ 120 | 121 | root_inited = False 122 | Error = logging.ERROR 123 | Warning = logging.WARNING 124 | Info = logging.INFO 125 | Debug = logging.DEBUG 126 | 127 | def __init__(self, name='fandango.Logger', parent=None,format='%(levelname)-8s %(asctime)s %(name)s: %(message)s',use_tango=True,use_print=True,level='INFO',max_len=0): 128 | self.max_len = max_len 129 | self.call__init__(Object) 130 | self.__levelAliases = {'ERROR':self.Error,'WARNING':self.Warning,'INFO':self.Info,'DEBUG':self.Debug} 131 | 132 | self.log_name = name 133 | if parent is not None: 134 | self.full_name = '%s.%s' % (parent.full_name, name) 135 | else: 136 | self.full_name = name 137 | 138 | self.log_obj = logging.getLogger(self.full_name) 139 | self.log_handlers = [] 140 | 141 | self.use_tango = use_tango and hasattr(self,'debug_stream') 142 | self._ForcePrint = use_print 143 | 144 | self.parent = None 145 | self.children = [] 146 | if parent is not None: 147 | self.parent = weakref.ref(parent) 148 | parent.addChild(self) 149 | self.setLogLevel(level) 150 | 151 | if not Logger.root_inited: 152 | #print 'log format is ',format 153 | self.initRoot(format) 154 | Logger.root_inited = True 155 | 156 | def __del__(self): 157 | parent = self.getParent() 158 | if parent is not None: 159 | parent.delChild(self) 160 | 161 | def initRoot(self,_format='%(threadName)-12s %(levelname)-8s %(asctime)s %(name)s: %(message)s'): 162 | logging.basicConfig(level=logging.INFO,format=_format) 163 | #logging.basicConfig(level=logging.DEBUG, 164 | # format='%(threadName)-12s %(levelname)-8s %(asctime)s %(name)s: %(message)s') 165 | 166 | def setLogPrint(self,force): 167 | ''' This method enables/disables a print to be executed for each log call ''' 168 | self._ForcePrint=force 169 | 170 | def getTimeString(self,t=None): 171 | if t is None: t=time.time() 172 | cad='%Y-%m-%d %H:%M:%S' 173 | s = time.strftime(cad,time.localtime(t)) 174 | ms = int((t-int(t))*1e3) 175 | return '%s.%d'%(s,ms) 176 | 177 | def logPrint(self,prio,msg): 178 | name = self.log_name+'.' if self.log_name else '' 179 | l = self.__levelAliases.get(prio,prio) 180 | if l0: msg = shortstr(msg,self.max_len) 291 | msg = str(msg).replace('\r','').replace('%','%%') 292 | obj = self.getTangoLog() 293 | if obj: 294 | stream = (obj.error_stream,obj.warn_stream,obj.info_stream,obj.debug_stream)[prio] 295 | stream(msg, *args, **kw) 296 | elif self._ForcePrint: 297 | self.logPrint(level.upper(),msg) 298 | else: 299 | stream = (self.log_obj.error,self.log_obj.warning,self.log_obj.info,self.log_obj.debug)[prio] 300 | stream(msg, *args, **kw) 301 | except Exception,e: 302 | print 'Exception in Logger.%s! \nmsg:%s\ne:%s\nargs:%s\nkw:%s'%(level,msg,e,str(args),str(kw)) 303 | print traceback.format_exc() 304 | 305 | 306 | def deprecated(self, msg, *args, **kw): 307 | filename, lineno, func = self.log_obj.findCaller() 308 | depr_msg = warnings.formatwarning(msg, DeprecationWarning, filename, lineno) 309 | self.log_obj.warning(depr_msg, *args, **kw) 310 | 311 | def flushOutput(self): 312 | self.syncLog() 313 | 314 | def syncLog(self): 315 | logger = self 316 | synced = [] 317 | while logger is not None: 318 | for handler in logger.log_handlers: 319 | if handler in synced: 320 | continue 321 | try: 322 | sync = getattr(handler, 'sync') 323 | except: 324 | continue 325 | sync() 326 | synced.append(handler) 327 | logger = logger.getParent() 328 | 329 | def changeLogName(self,name): 330 | """Change the log name.""" 331 | p = self.getParent() 332 | if p is not None: 333 | self.full_name = '%s.%s' % (p.full_name, name) 334 | else: 335 | self.full_name = name 336 | 337 | self.log_obj = logging.getLogger(self.full_name) 338 | for handler in self.log_handlers: 339 | self.log_obj.addHandler(handler) 340 | 341 | for child in self.getChildren(): 342 | self.changeLogName(child.log_name) 343 | 344 | class LogFilter(logging.Filter): 345 | 346 | def __init__(self, level): 347 | self.filter_level = level 348 | logging.Filter.__init__(self) 349 | 350 | def filter(self, record): 351 | ok = (record.levelno == self.filter_level) 352 | return ok 353 | -------------------------------------------------------------------------------- /fandango/callbacks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | """ 3 | ############################################################################# 4 | ## 5 | ## file : callbacks..py 6 | ## 7 | ## description : This class manages a list of attributes subscribed to events that could have multiple receivers each one. 8 | ## It supplies the ATK AttributeList behaviour. 9 | ## device.DevChild and those inherited classes depends on that. 10 | ## Global objects are: 11 | ## _EventsList, _EventReceivers, _StatesList, _AttributesList, GlobalCallback 12 | ## ... _EventReceivers must be substituted by DevicesList 13 | ## 14 | ## project : Tango Control System 15 | ## 16 | ## $Author: Sergi Rubio Manrique, srubio@cells.es $ 17 | ## 18 | ## $Revision: 2008 $ 19 | ## 20 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 21 | ## Bellaterra 22 | ## Spain 23 | ## 24 | ############################################################################# 25 | ## 26 | ## This file is part of Tango Control System 27 | ## 28 | ## Tango Control System is free software; you can redistribute it and/or 29 | ## modify it under the terms of the GNU General Public License as published 30 | ## by the Free Software Foundation; either version 3 of the License, or 31 | ## (at your option) any later version. 32 | ## 33 | ## Tango Control System is distributed in the hope that it will be useful, 34 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 35 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 36 | ## GNU General Public License for more details. 37 | ## 38 | ## You should have received a copy of the GNU General Public License 39 | ## along with this program; if not, see . 40 | ########################################################################### 41 | """ 42 | 43 | import PyTango 44 | import sys 45 | import os 46 | import time 47 | import threading 48 | import re 49 | from copy import * 50 | from excepts import getLastException 51 | 52 | """ 53 | @package callbacks 54 | 55 | @par Internal Variables 56 | @li _EventsList[attr_name] the last event received for each attribute 57 | @li _StatesList[dev_name] keeps the last State read for each device 58 | @li _AttributesList[dev_name] keeps the last AttributeValue struct for each attribute. 59 | 60 | @remarks Mostly used by PyStateComposer device 61 | 62 | It provides persistent storage. 63 | lock.acquire and lock.release should be used to prevent threading problems!, 64 | Use of the lists inside push_event is safe 65 | """ 66 | 67 | _EventsList = {} 68 | _EventReceivers = {} 69 | _StatesList = {} 70 | _AttributesList = {} 71 | 72 | class EventStruct(): 73 | name = '' 74 | event = None 75 | receivers = [] 76 | 77 | class TAttr(EventStruct): 78 | """ 79 | This class is used to keep information about events received, 80 | example of usage inside device.DevChild 81 | """ 82 | def __init__(self,name,dev_name='',proxy=None,event_id=None): 83 | self.name=name 84 | self.event=None #This is the last event received 85 | self.event_id=event_id #This is the ID received when subscribing 86 | self.dp=proxy #This is the device proxy 87 | self.dev_name=dev_name 88 | self.receivers=[] #This is the list of composers that must receive this event 89 | self.attr_value=None 90 | self.State=PyTango.DevState.UNKNOWN 91 | def __str__(self): 92 | return str(name)+","+str(self.event)+","+TangoStates[self.State]+";" 93 | def set(self, event): 94 | self.event=event#copy(event) 95 | self.attr_value=self.event.attr_value 96 | 97 | #def command_queue(cmd_list,args_list=[],timeout=5000,threads=10): 98 | #''' executes a set of commands asynchronously with the specified timeout 99 | #''' 100 | #from threading import Thread 101 | #from Queue import Queue 102 | #if args_list and len(cmd_list)!=len(args_list): 103 | #raise Exception,'cmd_list and args_list lengths differ!' 104 | #num_threads = max(len(cmd_list),max_threads) 105 | #queue = Queue() 106 | #results = {} 107 | ##wraps system ping command 108 | #def pinger(i, q, r): 109 | #"""Pings subnet""" 110 | #wait = threading.Event().wait 111 | #while True: 112 | #wait(.3) 113 | #ip = q.get() 114 | 115 | #r[ip] = (not ret) 116 | #q.task_done() 117 | ##Spawn thread pool 118 | #for i in range(num_threads): 119 | #worker = Thread(target=pinger, args=(i, queue,results)) 120 | #worker.setDaemon(True) 121 | #worker.start() 122 | ##Place work in queue 123 | #for ip in ips: 124 | #queue.put(ip) 125 | ##Wait until worker threads are done to exit 126 | #queue.join() 127 | #return results 128 | 129 | 130 | class EventCallback(): 131 | """ 132 | It provides persistent storage. 133 | lock.acquire and lock.release should be used to prevent threading problems!, 134 | Use of the lists inside push_event is safe 135 | """ 136 | def __init__(self): 137 | self.lock=threading.RLock() 138 | self.TimeOutErrors=0 139 | self.NotifdExceptions=['OutOfSync','EventTimeout','NotificationServiceFailed'] 140 | 141 | def push_event(self,event): 142 | self.lock.acquire() 143 | try: 144 | #Pruning tango:$TANGO_HOST and other tags 145 | attr_name = '/'.join(event.attr_name.split('/')[-4:]) 146 | dev_name = hasattr(event.device,'name') and event.device.name() or event.device 147 | print "in EventCallback.push_event(",dev_name,": ",attr_name,")" 148 | if not event.err and event.attr_value is not None: 149 | print "in EventCallback.push_event(...): ",attr_name,"=", event.attr_value.value 150 | self.TimeOutErrors=0 151 | _EventsList[attr_name.lower()].set(event) 152 | if attr_name.lower().endswith('/state'): 153 | _StatesList[dev_name.lower()]=event.attr_value.value 154 | _AttributesList[event.attr_name.lower()]=event.attr_value 155 | else: 156 | print 'in EventCallback.push_event(...): Received an Error Event!: ',event.errors 157 | _EventsList[attr_name.lower()].set(event) 158 | #if 'OutOfSync' in event.errors[0]['reason']: or 'API_EventTimeout' in event.errors[0]['reason']: 159 | #if [e for e in event.errors if hasattr(e,'keys') and 'reason' in e.keys() and any(re.search(exp,e['reason']) for exp in self.NotifdExceptions)]: 160 | reasons = [getattr(e,'reason',e.get('reason',str(e)) if hasattr(e,'get') else str(e)) for e in event.errors] #Needed to catch both PyTango3 and PyTango7 exceptions 161 | if any(n in r for n in self.NotifdExceptions for r in reasons): 162 | print 'callbacks=> DISCARDED EVENT FOR NOTIFD REASONS!!! %s(%s)' \ 163 | %(dev_name,reasons) 164 | self.TimeOutErrors+=1 165 | self.lock.release() 166 | return 167 | else: 168 | self.TimeOutErrors=0 169 | if attr_name.lower().endswith('/state'): 170 | _StatesList[dev_name.lower()]=None #An unreaded State cannot be UNKNOWN, it must be None to notify that an exception occurred! 171 | _AttributesList[attr_name.lower()]=None 172 | #Launching Device.push_event() 173 | for rec in _EventsList[attr_name].receivers: 174 | if rec in _EventReceivers.keys(): _EventReceivers[rec].push_event(event) 175 | elif hasattr(rec,'push_event'): rec.push_event(_event) 176 | elif isinstance(rec,threading.Event): rec.set() 177 | elif callable(rec): rec() 178 | else: raise 'UnknownEventReceiverType' 179 | except Exception,e: 180 | print 'exception in EventCallback.push_event(): ',e, ";", getLastException() 181 | self.lock.release() 182 | 183 | #THIS IS THE EVENTS CALLBACK SINGLETONE: 184 | GlobalCallback = EventCallback() 185 | 186 | ## @TODO ... implemented in addTAttr and addReceiver ... missing a dp attribute to finish the work 187 | #def subscribeToAttribute(subscriber,att_name): 188 | #""" 189 | #subscriber: a DeviceImpl object or the name of an already subscribed object 190 | #attribute: the FULL_NAME of the attribute to subscribe 191 | #""" 192 | #if att_name.count('/')<3: raise 'subscribeToAttribute_IncompleteAttributeName' 193 | #if isinstance(subscriber,PyTango.DeviceImpl): 194 | #EventReceivers[subscriber.get_name()]=subscriber 195 | #elif isinstance(subscriber,str): 196 | #subscriber=_EventReceivers[subscriber] 197 | #else: raise 'subscribeToAttribute_UnknownSubscriberException' 198 | 199 | #if not att_name in _EventsList.keys(): 200 | #print 'subscribeToAttribute(%s,%s)'%(subscriber.get_name(),att_name) 201 | #EventsList[att_name] = TAttr(att_name) 202 | #EventsList[att_name].receivers.append(subscriber) 203 | #EventsList[att_name].event_id = self.dp.subscribe_event(att,PyTango.EventType.CHANGE_EVENT,GlobalCallback,[],True) 204 | #EventsList[att_name].dev_name = att_name.rsplit('/',0) 205 | #AttributesList[att_name]=None #it could be done inside _EventsList?!?!? ... or could AttributeList substitute _EventsList? 206 | 207 | ##It will not be initialized here ... as it differs in DevChild an DevsList 208 | ##EventsList[att_name].dp = self.dp 209 | #if att=='State': #DevsList should substitute that 210 | #StatesList[_EventsList[att_name].dev_name]=PyTango.DevState.UNKNOWN 211 | 212 | #print "In ", self.get_name(), "::check_dp_attributes()", ": Listing Device/Attributes in _EventsList:" 213 | #for a,t in _EventsList.items(): print "\tAttribute: ",a,"\tDevice: ",t.dev_name,"\n" 214 | #else: 215 | #print "In ", self.get_name(), "::check_dp_attributes(",att_name,")", ": This attribute is already in the list, adding composer to receivers list." 216 | #if not subscriber.get_name() in _EventsList[att_name].receivers and not subscriber in _EventsList[att_name].receivers: 217 | #EventsList[att_name].receivers.append(subscriber) 218 | #pass 219 | 220 | def inStatesList(devname): 221 | print 'In callbacks.inStatesList ...' 222 | GlobalCallback.lock.acquire() 223 | print 'Checking if %s in %s.'%(devname,str(_StatesList.keys())) 224 | value=bool(devname.lower() in _StatesList.keys()) 225 | GlobalCallback.lock.release() 226 | return bool 227 | 228 | def getStateFor(devname): 229 | print 'In callbacks.getStateFor ...' 230 | GlobalCallback.lock.acquire() 231 | state = _StatesList[devname.lower()] if devname.lower() in _StatesList.keys() else None 232 | GlobalCallback.lock.release() 233 | return state 234 | 235 | def setStateFor(devname,state): 236 | print 'In callbacks.setStateFor ...' 237 | GlobalCallback.lock.acquire() 238 | _StatesList[devname.lower()]=state 239 | GlobalCallback.lock.release() 240 | return state 241 | 242 | def setAttributeValue(attr_name,attr_value): 243 | print 'In callbacks.setAttributeValue(%s)'%attr_name 244 | GlobalCallback.lock.acquire() 245 | _AttributesList[attr_name.lower()]=attr_value 246 | GlobalCallback.lock.release() 247 | return attr_value 248 | 249 | def inAttributesList(attname): 250 | GlobalCallback.lock.acquire() 251 | value=bool(attname.lower() in _AttributesList.keys()) 252 | GlobalCallback.lock.release() 253 | return bool 254 | 255 | def getAttrValueFor(attname): 256 | GlobalCallback.lock.acquire() 257 | value=_AttributesList[attname.lower()] 258 | GlobalCallback.lock.release() 259 | return value 260 | 261 | def inEventsList(attname): 262 | GlobalCallback.lock.acquire() 263 | value=bool(attname.lower() in _EventsList.keys()) 264 | GlobalCallback.lock.release() 265 | return value 266 | 267 | def getEventFor(attname): 268 | GlobalCallback.lock.acquire() 269 | event=_EventsList[attname.lower()] 270 | GlobalCallback.lock.release() 271 | return event 272 | 273 | def getEventItems(): 274 | GlobalCallback.lock.acquire() 275 | result = _EventsList.items() 276 | GlobalCallback.lock.release() 277 | return result 278 | 279 | def getSubscribedItems(receiver): 280 | '''It returns a list with all devices managed by callbacks to which this receiver is effectively subscribed''' 281 | GlobalCallback.lock.acquire() 282 | result = [] 283 | for ev in _EventsList.values(): 284 | if receiver in ev.receivers: 285 | result.append (ev.dev_name) 286 | GlobalCallback.lock.release() 287 | return result 288 | 289 | 290 | def addTAttr(tattr): 291 | try: 292 | GlobalCallback.lock.acquire() 293 | att_name = tattr.name.lower() 294 | _EventsList[att_name] = tattr 295 | _AttributesList[att_name]=None 296 | if att_name.endswith=='/state': 297 | _StatesList[tattr.dev_name.lower()]=None 298 | except: print traceback.format_exc() 299 | finally: GlobalCallback.lock.release() 300 | return 301 | 302 | def addReceiver(attribute,receiver): 303 | try: 304 | GlobalCallback.lock.acquire() 305 | if not receiver.lower() in _EventsList[attribute.lower()].receivers: 306 | _EventsList[attribute.lower()].receivers.append(receiver.lower()) 307 | except: print traceback.format_exc() 308 | finally: GlobalCallback.lock.release() 309 | return 310 | 311 | def subscribeDeviceAttributes(self,dev_name,attrs): 312 | """ This is how attributes were registered in the old PyStateComposer """ 313 | dev = PyTango.DeviceProxy(dev_name) 314 | dev.ping() 315 | # Initializing lists 316 | if dev_name not in callbacks._StatesList: callbacks._StatesList[dev_name] = PyTango.DevState.UNKNOWN 317 | if dev_name not in callbacks._AttributesList: callbacks._AttributesList[dev_name] = None 318 | 319 | for att in attrs: 320 | att_name = (dev_name+'/'+att).lower() 321 | if att not in dev.get_attribute_list(): 322 | continue 323 | if not dev.is_attribute_polled(att) and self.ForcePolling: 324 | self.info('::AddDevice(): forcing %s polling to %s' % (att,argin)) 325 | period = dev.get_attribute_poll_period(att) or 3000 326 | dev.poll_attribute(att,period) 327 | self.debug("%s.poll_attribute(%s,%s)" % (argin,att,period)) 328 | #cb = self 329 | cb = GlobalCallback 330 | 331 | if not att_name in callbacks._EventsList.keys(): 332 | callbacks._EventsList[att_name] = self.TAttr(att_name) 333 | callbacks._EventsList[att_name].receivers.append(self.get_name()) 334 | self.info('AddDevice: subscribing event for %s' % att_name) 335 | event_id = dev.subscribe_event(att,PyTango.EventType.CHANGE,cb,[],True) 336 | # Global List 337 | callbacks._EventsList[att_name].dp = dev 338 | callbacks._EventsList[att_name].event_id = event_id 339 | callbacks._EventsList[att_name].dev_name = dev_name 340 | print "In ", self.get_name(), "::AddDevice()", ": Listing Device/Attributes in _EventsList:" 341 | for a,t in callbacks._EventsList.items(): print "\tAttribute: ",a,"\tDevice: ",t.dev_name,"\n" 342 | else: 343 | self.warning("::AddDevice(%s): This attribute is already in the list, adding composer to receivers list." % att_name) 344 | if not dev_name in callbacks._EventsList[att_name].receivers: 345 | callbacks._EventsList[att_name].receivers.append(self.get_name()) 346 | if callbacks._EventsList[att_name].attr_value is not None: 347 | if att is 'State': 348 | callbacks._StatesList[dev_name]=_EventsList[att_name].attr_value.value 349 | else: 350 | callbacks._AttributesList[dev_name]=_EventsList[att_name].attr_value.value 351 | return 352 | 353 | -------------------------------------------------------------------------------- /fandango/linos.py: -------------------------------------------------------------------------------- 1 | 2 | #some Linux utilities 3 | 4 | """ 5 |
      6 | Executing shell commands and getting the stdout
      7 | 
      8 | The module subprocess must be used for that, instead of os.exec* or os.system.
      9 | 
     10 | import subprocess
     11 | ps = subprocess.Popen('ps uax',shell=True,stdout=subprocess.PIPE)
     12 | grep1 = subprocess.Popen('grep -i hdbarchiver',shell=True,stdin=ps.stdout,stdout=subprocess.PIPE)
     13 | grep1.communicate()
     14 | Out[77]:
     15 | ('sicilia  13698  0.0  0.0  51000  1552 ?        Ss   Feb23   0:00 SCREEN -dm -S HdbArchiver-palantir01_BO01_VC /homelocal/sicilia/ap\nsicilia   6343  0.0  0.0  50872  2748 pts/13   S+   10:17   0:00 screen -r HdbArchiver-palantir01_BO01_VC\n',
     16 |  None)
     17 | 
     18 | 
    19 | """ 20 | 21 | import time,sys,os,re,traceback 22 | import fandango.objects as fun #objects module includes functional 23 | 24 | ################################################################################3 25 | # Shell methods 26 | 27 | def shell_command(*commands, **keywords): 28 | """Executes a list of commands linking their stdin and stdouts 29 | @param commands each argument is interpreted as a command 30 | @param split returns stdout as a file.readlines() result 31 | @return the last stdout result""" 32 | if not commands: return 33 | elif isinstance(commands,str): commands=[commands] 34 | split = keywords and 'split' in keywords and keywords['split'] 35 | import subprocess 36 | process = [subprocess.Popen(commands[0],shell=True,stdout=subprocess.PIPE)] 37 | if len(commands)>1: 38 | for comm in commands[1:]: 39 | ps = subprocess.Popen(comm,shell=True,stdin=process[-1].stdout,stdout=subprocess.PIPE) 40 | process.append(ps) #comm1 | comm2 | comm3 > result 41 | result = process[-1].communicate() 42 | result = (result and len(result)>=1 and result[0]) or None #process returns a tuple, being stdout the first field 43 | 44 | ## @remark I know that it could fit in one line ... but I prefer to make it readable 45 | if not result: return 46 | elif split: return result.split('\n') 47 | else: return result 48 | 49 | def sendmail(subject,text,receivers,sender='',attachments=None,trace=False): 50 | # method for sending mails 51 | if trace: print 'Sending mail to %s'%receivers 52 | chars = sorted(set(re.findall('[^a-zA-Z0-9/\ \n\.\=\(\),\[\]_\-]',text))) 53 | for c in chars: 54 | if c in text and '\\'+c not in text: 55 | text = text.replace(c,'\\'+c) 56 | if '\n' in text and '\\n' not in text: 57 | text = text.replace('\n','\\n') 58 | receivers = ' '+(receivers if isinstance(receivers,str) else ' '.join(receivers)) 59 | sender = sender and " -r %s"%sender 60 | attachments = attachments and ' '+' '.join('-a %s'%a for a in attachments) or '' 61 | command = 'echo -e "'+text+'" ' 62 | command += '| mail -s "%s" ' % subject 63 | #command += '-S from=%s ' % self.FromAddress #'-r %s ' % (self.FromAddress) 64 | #command += (MAIL_RECEIVER if fandango.isString(MAIL_RECEIVER) else ','.join(MAIL_RECEIVER)) 65 | command += sender + attachments + receivers 66 | if trace: print(command.rsplit('|',1)[-1]) 67 | os.system(command) 68 | 69 | ################################################################################3 70 | # Platform/Architecture/Hostname methods 71 | 72 | class MyMachine(fun.Struct): 73 | """ This method identifies the current Machine (OS/Arch/Hostname/Kernel) using the platform module """ 74 | def __init__(self): 75 | import platform 76 | self.hostname = self.host = platform.node() 77 | self.dist = platform.dist() 78 | self.arch = platform.machine() 79 | self.kernel = platform.release() 80 | self.os = platform.system() 81 | self.platform = platform.platform() 82 | 83 | ################################################################################3 84 | # Processes methods 85 | 86 | 87 | def get_memstats(units='m'): 88 | """ 89 | This method returns mem stats in Megabytes 90 | Remember that in linux buffers and cached memory should be considered FREE 91 | Dictionary returned is like: 92 | { 'buffers': '6', 93 | 'cached': '557', 94 | 'free': '16', 95 | 'shared': '0', 96 | 'total': '1002', 97 | 'used': '986' 98 | } 99 | """ 100 | txt = shell_command('free -%s'%units) 101 | txt = txt.split('\n') 102 | memstats = dict(zip(txt[0].strip().split(),map(int,txt[1].strip().split()[1:]))) 103 | return memstats 104 | 105 | def get_free_memory(units='m'): 106 | stats = get_memstats(units) 107 | return stats['buffers']+stats['cached']+stats['free'] 108 | 109 | def get_memory_usage(): 110 | """This method returns the percentage of total memory used in this machine""" 111 | stats = get_memstats() 112 | mfree = float(stats['buffers']+stats['cached']+stats['free']) 113 | return 1-(mfree/stats['total']) 114 | 115 | def get_memory(pid=None,virtual=False): 116 | """This function uses '/proc/pid/status' to get the memory consumption of a process """ 117 | try: 118 | if pid is None: pid = os.getpid() 119 | mem,units = shell_command('cat /proc/%s/status | grep Vm%s'%(pid,'Size' if virtual else 'RSS')).lower().strip().split()[1:3] 120 | return int(mem)*(1e3 if 'k' in units else (1e6 if 'm' in units else 1)) 121 | except: 122 | print traceback.format_exc() 123 | return 0 124 | 125 | def get_cpu(pid): 126 | """ Uses ps to get the CPU usage of a process by PID ; it will trigger exception of PID doesn't exist """ 127 | return float(linos.shell_command('ps h -p %d -o pcpu'%pid)) 128 | 129 | def get_process_pid(include='',exclude='grep|screen|kwrite'): 130 | if not include: return os.getpid() 131 | include = include.replace(' ','.*') 132 | exclude = exclude.replace(' ','.*') 133 | ps = shell_command('ps ax | grep -E "%s"'%include+(' | grep -viE "%s"'%exclude if exclude else '')) 134 | if not ps: 135 | return None #raise Exception('No matching process found') 136 | lines = [s.strip() for s in ps.split('\n')] 137 | print '\n'.join(lines) 138 | pids = [] 139 | for l in lines: 140 | for p in l.split(): 141 | if re.match('[0-9]+',p): 142 | pids.append(int(p)) 143 | break 144 | if len(pids)>1: 145 | raise Exception('Multiple PIDs found: please refine your search using exclude argument') 146 | return pids[0] 147 | 148 | def check_process(pid): 149 | try: return file_exists('/proc/%s'%pid) #os.kill(pid,0) 150 | except: return False 151 | 152 | def kill_process(process=None,signal=15): 153 | pid = process if fun.isNumber(process) else get_process_pid(process) 154 | os.kill(pid,signal) 155 | 156 | def KillEmAll(klass): 157 | processes = shell_command('ps uax').split('\n') 158 | processes = [s for s in processes if '%s'%(klass) in s] 159 | for a in processes: 160 | print 'Killing %s' % a 161 | pid = a.split()[1] 162 | shell_command('kill -9 %s'%pid) 163 | 164 | ################################################################################3 165 | # Filesystem methods 166 | 167 | import os,stat,time 168 | 169 | def is_dir(path): 170 | return stat.S_ISDIR(os.stat(path)[stat.ST_MODE]) 171 | 172 | def is_link(path): 173 | mode = os.lstat(path).st_mode 174 | return stat.S_ISLNK(mode) 175 | 176 | def file_exists(path): 177 | try: 178 | os.stat(path) 179 | return True 180 | except: 181 | return False 182 | 183 | def get_file_size(path): 184 | return os.stat(path)[stat.ST_SIZE] 185 | 186 | def listdir(folder,mask='.*',files=False,folders=False,links=False): 187 | try: 188 | if folders and not files: 189 | vals = os.walk(folder,followlinks=links).next()[1] 190 | elif files and not folders: 191 | vals = os.walk(folder,followlinks=links).next()[2] 192 | else: 193 | vals = os.listdir(folder) 194 | if mask: 195 | return [f for f in vals if re.match(fun.toRegexp(mask),f)] 196 | else: 197 | return vals 198 | except Exception,e: 199 | print e 200 | raise Exception('FolderDoesNotExist',folder) 201 | 202 | def copydir(origin,destination,timewait=0.1,overwrite=False): 203 | """ 204 | This method copies recursively a folder, creating subdirectories if needed and exiting at first error. 205 | Origin and destination must be existing folders. 206 | It includes a timewait between file copying 207 | """ 208 | if not file_exists(origin): return 209 | if not is_dir(origin): return 210 | fs = listdir(origin) 211 | print '%s dir contains %d files' % (origin,len(fs)) 212 | 213 | def exec_(com): 214 | print(com) 215 | r = os.system(com) 216 | assert not r, 'OS Error %s returned by: %s'%(r,com) 217 | 218 | if not file_exists(destination): 219 | exec_('mkdir "%s"'%(destination)) 220 | 221 | for f in sorted(fs): 222 | if is_dir('%s/%s'%(origin,f)): 223 | #if the file to process is a directory, we create it and process it 224 | copydir('%s/%s'%(origin,f),'%s/%s'%(destination,f),timewait) 225 | else: 226 | #if it was not a directory, we copy it 227 | if not overwrite and file_exists('%s/%s'%(destination,f)): 228 | print '\t%s/%s already exists'%(destination,f) 229 | continue 230 | size,t0 = file_size('%s/%s'%(origin,f)),time.time() 231 | exec_('cp "%s/%s" "%s/"'%(origin,f,destination)) 232 | t1 = time.time() 233 | if t1>t0: print('\t%f b/s'%(size/(t1-t0))) 234 | time.sleep(timewait) 235 | 236 | def diffdir(origin,destination,caseless=False,checksize=True): 237 | fs,nfs = listdir(origin),listdir(destination) 238 | lfs,lnfs = [s.lower() for s in fs],[n.lower() for n in nfs] 239 | missing = [] 240 | for f in sorted(fs): 241 | df = '%s/%s'%(origin,f) 242 | if not (f in nfs) and (not caseless or not f.lower() in lnfs): 243 | print '> %s/%s not in %s'%(origin,f,destination) 244 | missing.append(df) 245 | elif is_dir(df): 246 | missing.extend(diffdir(df,'%s/%s'%(destination,f))) 247 | elif checksize: 248 | if file_size(df)!=file_size('%s/%s'%(destination,f)): 249 | print '---> %s and %s differs!'%(df,'%s/%s'%(destination,nf)) 250 | missing.append(df) 251 | for n in sorted(nfs): 252 | if not (n in fs) and (not caseless or not n.lower() in lfs): 253 | print '>> %s/%s not in %s'%(destination,n,origin) 254 | return missing 255 | 256 | def findfolders(target='',parent='',filter_=True,printout = False): 257 | import os,fandango,stat,re,sys 258 | result = [] 259 | if not parent: parent = os.getcwd() 260 | 261 | if filter_: 262 | filter_folders = lambda fs: [f for f in fs if f not in '.svn tags branches'.split() and (f.count('/')<6 or not f in 'trunk xpand doc'.split())] 263 | else: filter_folders = lambda fs: fs 264 | 265 | def get_folders(path): 266 | folders = ['%s/%s'%(path,f) for f in filter_folders(linos.listdir(path,folders=True)) if not stat.S_ISLNK(os.lstat('%s/%s'%(path,f)).st_mode)] 267 | for f in folders: 268 | folders.extend(get_folders(f)) 269 | return folders 270 | 271 | for f in get_folders(): 272 | if not target or target.lower() in f.lower(): 273 | if printout: print f 274 | result.append(f) 275 | return result 276 | 277 | ################################################################################3 278 | # Kde methods 279 | 280 | def desktop_switcher(period,event=None,iterations=2): 281 | """ It uses wmctrl to switch between all desktops with a period as specified. 282 | @param period Time between desktop changes 283 | @param event Event to stop the application 284 | @param iterations Number of cycles to execute, -1 for infinite 285 | """ 286 | import threading 287 | if not event: 288 | event = threading.Event() 289 | def run(): 290 | ndesks = len([d for d in shell_command('wmctrl -d',split=True) if d]) 291 | i=0 292 | while True: 293 | if iterations>0 and i>=ndesks*iterations or event.isSet(): 294 | break 295 | else: i+=1 296 | shell_command('wmctrl -s %d'%(i%ndesks)) 297 | event.wait(period) 298 | return event.isSet() 299 | thr = threading.Thread(target=run) 300 | thr.daemon = True #If it is false means that Python will exit if this is the last unfinished thread 301 | thr.start() 302 | return True 303 | 304 | ################################################################################3 305 | # Networking methods 306 | 307 | def ping(ips,threaded = False, timeout = 1): 308 | ''' By Noah Gift's, PyCon 2008 309 | ips = ['ivc%02d01'%(i+1) for i in range(16)] 310 | #ips = ["10.0.1.1", "10.0.1.3", "10.0.1.11", "10.0.1.51"] 311 | ''' 312 | import subprocess 313 | if isinstance(ips,str): ips=[ips] 314 | def _ping(ip): 315 | return not subprocess.call("ping -c 1 -w %d %s" % (int(max((1,timeout))),ip), 316 | shell=True, 317 | stdout=open('/dev/null', 'w'), 318 | stderr=subprocess.STDOUT) 319 | if not threaded: 320 | return dict((ip,_ping(ip)) for ip in ips) 321 | else: 322 | from threading import Thread,Event 323 | from Queue import Queue 324 | num_threads,event = 4,Event() 325 | pool,queue,results = [],Queue(),{} 326 | #wraps system ping command 327 | ## WARNING ... THIS IMPLEMENTATION OF THE THREAD IS GIVING A LOT OF PROBLEMS DUE TO THREAD NOT CLOSING! (waiting at q.get()) 328 | def pinger(i, q, r): 329 | """Pings subnet""" 330 | while not event.isSet(): 331 | ip = q.get() 332 | #print "Thread %s: Pinging %s" % (i, ip) 333 | ret = _ping(ip) 334 | #if ret == 0: print "%s: is alive" % ip 335 | #else: print "%s: did not respond" % ip 336 | r[ip] = (not ret) 337 | q.task_done() 338 | event.wait(.01) 339 | #Spawn thread pool 340 | for i in range(num_threads): 341 | pool.append(Thread(target=pinger, args=(i, queue,results))) 342 | pool[-1].setDaemon(True) 343 | pool[-1].start() 344 | #Place work in queue 345 | for ip in ips: queue.put(ip) 346 | queue.join() #Wait until worker threads are done to exit 347 | event.set(),event.wait(.01) 348 | #[t.join() for t in pool] 349 | return results 350 | 351 | #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 352 | # time methods 353 | #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 354 | import time 355 | 356 | def timefun(fun): 357 | """ This function allow to get time spent by some method calls, use timefun(lambda:f(args)) if needed """ 358 | now = time.time() 359 | result = fun() 360 | return (time.time()-now,result) 361 | 362 | #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 363 | # Managing arguments 364 | #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 365 | import sys 366 | 367 | def sysargs_to_dict(args=None,defaults=[],trace=False,split=False,cast=True): 368 | ''' 369 | It parses the command line arguments into an understandable dict 370 | defaults is the list of anonymous arguments to accept (would be False if not specified) 371 | 372 | > command H=1 --option=value --parameter VALUE -test default_arg1 default_arg2 373 | 374 | will returns 375 | 376 | {H:1,option:value,parameter:VALUE,test:True,params:[default_arg1,default_arg2]} 377 | 378 | getopt and argparse modules in python provide similar functionality, but are not available in all of our distributions 379 | ''' 380 | if args is None: args = sys.argv[1:] 381 | if trace: print 'sysargs_to_dict(%s,%s)'%(args,defaults) 382 | result,defargs,vargs = {},[],[] 383 | cast_arg = lambda x: fun.str2type(x,use_eval=True) if cast else x 384 | 385 | ##Separate parameter/options and default arguments 386 | [(vargs if ('=' in a or a.startswith('-') or (i and args[i-1].startswith('--') and '=' not in args[-1])) else defargs).append(a) 387 | for i,a in enumerate(args)] 388 | defargs = map(cast_arg,defargs) 389 | if trace: print('defargs: %s'%defargs) 390 | for n,a in enumerate(vargs): 391 | if '=' in a: #argument like [-]ARG=VALUE 392 | while a.startswith('-'): a = a[1:] 393 | if a: result[a.split('=',1)[0]] = cast_arg(a.split('=',1)[1]) 394 | elif a.startswith('--'): #argument with - prefix 395 | while a.startswith('-'): a = a[1:] 396 | if not a: continue 397 | #If it is not the last value it is considered an assigment 398 | if (n+1)len(defaults): result[None] = defargs[len(defaults):] 418 | result.update(zip(defaults,defargs)) 419 | result.update((d,False) for d in defaults if d not in result) 420 | 421 | if trace: print result 422 | if len(result)==1 and None in result: split = True 423 | if not split: 424 | return result 425 | else: 426 | args = result.pop(None,[]) 427 | kwargs = result 428 | return args,kwargs 429 | 430 | def arg_to_bool(arg): 431 | if type(arg) is str: 432 | return arg.lower() in ('true','yes') or False 433 | else: 434 | return bool(arg) 435 | 436 | def expand_args_to_list(args,type_=int): 437 | """ it generates a list of args from a sequence like 1,3,5 or 3-7 """ 438 | result = [] 439 | for arg in args.split(',' in args and ',' or ' '): 440 | if type_ is int and '-' in arg: 441 | vals = [type_(v) for v in arg.split('-',1)] 442 | result.extend(range(*(vals[0],vals[1]+1))) 443 | elif type_ is int and ':' in arg: 444 | vals = [type_(v) for v in arg.split(':',2)] 445 | result.extend(range(*(vals[0],vals[1]+1,vals[2]))) 446 | else: result.append(type_(arg)) 447 | return result 448 | 449 | expand_args_to_int_list = lambda args: expand_args_to_list(args,int) 450 | expand_args_to_str_list = lambda args: expand_args_to_list(args,str) 451 | 452 | if __name__ == '__main__': 453 | import sys 454 | #print sysargs_to_dict(defaults=['params'],cast=False,split=True) 455 | print sysargs_to_dict.__name__,'\n',sysargs_to_dict.__doc__ 456 | print sysargs_to_dict(cast=False,split=True) 457 | -------------------------------------------------------------------------------- /fandango/device.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | """ @if gnuheader 3 | ############################################################################# 4 | ## 5 | ## file : device.py 6 | ## 7 | ## description : CLASS FOR Enhanced TANGO DEVICE SERVERs 8 | ## 9 | ## project : Tango Control System 10 | ## 11 | ## $Author: Sergi Rubio Manrique, srubio@cells.es $ 12 | ## 13 | ## $Revision: 2008 $ 14 | ## 15 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 16 | ## Bellaterra 17 | ## Spain 18 | ## 19 | ############################################################################# 20 | ## 21 | ## This file is part of Tango Control System 22 | ## 23 | ## Tango Control System is free software; you can redistribute it and/or 24 | ## modify it under the terms of the GNU General Public License as published 25 | ## by the Free Software Foundation; either version 3 of the License, or 26 | ## (at your option) any later version. 27 | ## 28 | ## Tango Control System is distributed in the hope that it will be useful, 29 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 30 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 31 | ## GNU General Public License for more details. 32 | ## 33 | ## You should have received a copy of the GNU General Public License 34 | ## along with this program; if not, see . 35 | ########################################################################### 36 | @endif 37 | @package device 38 | @brief provides Dev4Tango, StateQueue, DevChild 39 | @todo @warning IMPORTING THIS MODULE IS CAUSING SOME ERRORS WHEN CLOSING PYTHON DEVICE SERVERS, BE CAREFUL! 40 | 41 | This module is used to have all those classes used inside DeviceServers or to control/configure them 42 | and are not part of the Astor api (ServersDict) 43 | """ 44 | 45 | #pytango imports 46 | import PyTango 47 | import types 48 | #fandango imports 49 | import functional as fun 50 | from log import Logger,except2str,printf 51 | from excepts import * 52 | from callbacks import * 53 | import tango #Double import to avoid ambiguous naming of some methods 54 | from tango import * #USE_TAU imported here, get_polled_attrs and other useful methods 55 | from dicts import CaselessDefaultDict,CaselessDict 56 | from arrays import TimedQueue 57 | from dynamic import DynamicDS,USE_STATIC_METHODS 58 | 59 | 60 | ######################################################################################## 61 | ## Device servers template 62 | 63 | class Dev4Tango(PyTango.Device_4Impl,log.Logger): 64 | """ 65 | This class provides several new features to TangoDevice implementations. 66 | By including log.Logger it also includes objects.Object as parent class. 67 | It allows to use call__init__(self, klass, *args, **kw) to avoid multiple inheritance from same parent problems. 68 | Therefore, use self.call__init__(PyTango.Device_4Impl,cl,name) instead of PyTango.Device_4Impl.__init__(self,cl,name) 69 | 70 | It also allows to connect several devices within the same server or not usint taurus.core 71 | """ 72 | 73 | def trace(self,prio,s): 74 | printf( '4T.%s %s %s: %s' % (prio.upper(),time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()),self.get_name(),s)) 75 | 76 | ##@name State Machine methods 77 | #@{ 78 | 79 | def is_Attr_allowed(self, req_type): 80 | """ This method is a template for any Attribute allowed control. """ 81 | self.info( 'In is_Attr_allowed ...' ) 82 | return bool( self.get_state() not in [PyTango.DevState.UNKNOWN,PyTango.DevState.INIT] ) 83 | 84 | def set_state(self,state): 85 | self._state = state 86 | type(self).mro()[type(self).mro().index(Dev4Tango)+1].set_state(self,state) 87 | 88 | def get_state(self): 89 | #@Tango6 90 | #This have been overriden as it seemed not well managed when connecting devices in a same server 91 | if not hasattr(self,'_state'): self._state = PyTango.DevState.INIT 92 | if getattr(self,'UpdateAttributesThread',None) and 0 Subscribing attributes from an internal or non-tau device') 266 | for attribute in attributes: 267 | self.ExternalAttributes[(device+'/'+attribute).lower()] = fakeAttributeValue(attribute,None,parent=deviceObj,device=device) 268 | import threading 269 | if not hasattr(self,'Event'): self.Event = threading.Event() 270 | if not hasattr(self,'UpdateAttributesThread'): 271 | self.UpdateAttributesThread = threading.Thread(target=self.update_external_attributes) 272 | self.UpdateAttributesThread.setDaemon(True) 273 | self.UpdateAttributesThread.start() 274 | pass 275 | self.info('Registered attributes are: %s'%self.ExternalAttributes.keys()) 276 | 277 | def unsubscribe_external_attributes(self): 278 | try: 279 | if hasattr(self,'Event'): 280 | self.Event.set() 281 | if hasattr(self,'UpdateAttributesThread'): 282 | self.UpdateAttributesThread.join(getattr(self,'PollingCycle',3000.)) 283 | if getattr(self,'use_tau',False): 284 | from taurus import Attribute 285 | for at in self.ExternalAttributes.values(): 286 | if isinstance(at,Attribute): 287 | at.removeListener(self.event_received) 288 | except Exception,e: 289 | self.error('Dev4Tango.unsubscribe_external_attributes() failed: %s'%e) 290 | return 291 | 292 | def write_external_attribute(self,device,attribute,data): 293 | self.info('===================> In write_external_attribute(%s,%s)'%(device,attribute)) 294 | device,attribute = device.lower(),attribute.lower() 295 | deviceObj = self.get_devs_in_server().get(device,None) 296 | if deviceObj is None: 297 | if getattr(self,'use_tau',False): 298 | from taurus import Device 299 | Device(device).write_attribute(attribute,data) 300 | else: 301 | aname = str(device+'/'+attribute).lower() 302 | attr = self.ExternalAttributes[aname] 303 | if attr.parent is None: attr.parent = PyTango.DeviceProxy(device) 304 | attr.parent.write_attribute(attribute,data) 305 | else: 306 | if isinstance(deviceObj,DynamicDS): 307 | method = 'write_dyn_attr' 308 | deviceObj.myClass.DynDev = deviceObj 309 | else: 310 | method = 'write_%s'%attribute 311 | alloweds = [c for c in dir(deviceObj) if c.lower()=='is_%s_allowed'%attribute] 312 | is_allowed = not alloweds or getattr(deviceObj,alloweds[0])(PyTango.AttReqType.WRITE_REQ) 313 | if is_allowed: 314 | aname = (device+'/'+attribute).lower() 315 | attr = self.ExternalAttributes.get(aname,fakeAttributeValue(attribute)) 316 | attr.set_write_value(data) 317 | methods = [c for c in dir(deviceObj) if c.lower()==method] 318 | if methods: 319 | method = getattr(deviceObj,methods[0]) 320 | #if isinstance(deviceObj,DynamicDS) and USE_STATIC_METHODS: 321 | if isinstance(method,types.FunctionType): 322 | #method is staticmethod 323 | method(deviceObj,attr) 324 | else: method(attr) 325 | else: raise Exception('AttributeNotFound_%s!'%(device+'/'+attribute)) 326 | else: 327 | raise Exception('WriteNotAllowed_%s!'%(device+'/'+attribute)) 328 | 329 | def launch_external_command(self,device,target,argin=None): 330 | self.info('===================> In launch_external_command(%s,%s,%s)'%(device,target,argin)) 331 | device,target = device.lower(),target.lower() 332 | deviceObj = self.get_devs_in_server().get(device,None) 333 | if deviceObj is None: 334 | if getattr(self,'use_tau',False): 335 | from taurus import Device 336 | return Device(device).command_inout(target,argin) 337 | else: 338 | PyTango.DeviceProxy(device).command_inout(target,argin) 339 | else: 340 | alloweds = [c for c in dir(deviceObj) if c.lower()=='is_%s_allowed'%target] 341 | is_allowed = not alloweds or getattr(deviceObj,alloweds[0])() 342 | if is_allowed: 343 | command = [c for c in dir(deviceObj) if c.lower()==target] 344 | if command: getattr(deviceObj,command[0])(argin) 345 | else: raise Exception('CommandNotFound_%s/%s!'%(device,target)) 346 | else: 347 | raise Exception('CommandNotAllowed_%s!'%(device+'/'+target)) 348 | 349 | def update_external_attributes(self): 350 | """ This thread replaces TauEvent generation in case that the attributes are read from a device within the same server. """ 351 | while not self.Event.isSet(): 352 | waittime = 3. 353 | try: 354 | serverAttributes = [a for a,v in self.ExternalAttributes.items() if isinstance(v,fakeAttributeValue)] 355 | waittime = 1e-3*self.PollingCycle/(len(serverAttributes)+1) 356 | for aname in serverAttributes: 357 | self.last_update = time.time() 358 | try: 359 | if self.Event.isSet(): 360 | self.events_error = 'Thread finished by Event.set()' 361 | break 362 | attr = self.ExternalAttributes[aname] 363 | device,attribute = aname.rsplit('/',1) 364 | self.debug('====> updating values from %s(%s.%s) after %s s'%(type(attr.parent),device,attribute,waittime)) 365 | event_type = fakeEventType.lookup['Periodic'] 366 | try: 367 | attr.read(cache=False) 368 | except Exception,e: 369 | print '#'*80 370 | event_type = fakeEventType.lookup['Error'] 371 | self.events_error = except2str(e) #traceback.format_exc() 372 | print self.events_error 373 | 374 | self.info('Sending fake event: %s/%s = %s(%s)' % (device,attr.name,event_type,attr.value)) 375 | self.event_received(device+'/'+attr.name,event_type,attr) 376 | except: 377 | self.events_error = traceback.format_exc() 378 | print 'Exception in %s.update_attributes(%s): \n%s' % (self.get_name(),attr.name,self.events_error) 379 | self.Event.wait(waittime) 380 | #End of for 381 | self.Event.wait(waittime) 382 | except: 383 | self.events_error = traceback.format_exc() 384 | self.Event.wait(3.) 385 | print self.events_error 386 | print 'Exception in %s.update_attributes()' % (self.get_name()) 387 | #End of while 388 | self.info('%s.update_attributes finished') 389 | if not self.events_error.replace('None','').strip(): 390 | self.events_error = traceback.format_exc() 391 | return 392 | 393 | def event_received(self,source,type_,attr_value): 394 | """ 395 | This method manages the events received from external attributes. 396 | This method must be overriden in child classes. 397 | """ 398 | #self.info,debug,error,warning should not be used here to avoid conflicts with taurus.core logging 399 | def log(prio,s): print '%s %s %s: %s' % (prio.upper(),time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()),self.get_name(),s) 400 | self.last_event_received = time.time() 401 | log('info','In Dev4Tango.event_received(%s(%s),%s,%s) at %s'%(type(source).__name__,source,fakeEventType[type_],type(attr_value).__name__,self.last_event_received)) 402 | return 403 | 404 | ##@} 405 | 406 | ############################################################################################################## 407 | ## DDebug, DS used to have access to DServer internal objects (as a complement of DServer admin device 408 | 409 | from fandango.dynamic import DynamicDS,DynamicDSClass 410 | class DDebug(DynamicDS): 411 | def __init__(self,cl, name): 412 | U = PyTango.Util.instance() 413 | import gc,resource 414 | try: 415 | import guppy 416 | heapy = guppy.hpy() 417 | except:guppy,heapy = None,None 418 | DynamicDS.__init__(self,cl,name,_locals={'Util':U,'PyUtil':U,'self':self,'fandango':fandango, 419 | 'resource':resource,'gc':gc,'guppy':guppy,'heapy':heapy}, 420 | useDynStates=False) 421 | DDebug.init_device(self) 422 | def init_device(self): 423 | self.set_state(PyTango.DevState.ON) 424 | #self.get_DynDS_properties() 425 | def always_executed_hook(self): pass #DynamicDS.always_executed_hook(self) 426 | def read_attr_hardware(self,data): pass 427 | @staticmethod 428 | def addToServer(py,server,instance): 429 | name = '%s/%s'%(server,instance) 430 | add_new_device(name,'DDebug','sys/%s/%s_%s'%('DDebug',server,instance)) 431 | py.add_TgClass(DDebugClass,DDebug,'DDebug') 432 | 433 | class DDebugClass(DynamicDSClass): 434 | class_property_list={} 435 | device_property_list={} 436 | cmd_list={'evaluateFormula':[[PyTango.DevString, "formula to evaluate"],[PyTango.DevString, "formula to evaluate"],],} 437 | attr_list={} 438 | 439 | ############################################################################################################## 440 | ## DevChild ... DEPRECATED and replaced by Dev4Tango + TAU 441 | 442 | class DevChild(Dev4Tango): 443 | pass 444 | 445 | def pickle_device(dev_name): 446 | dp = get_device(dev_name) 447 | attrs = dp.get_attribute_list() 448 | commands = get_device_commands(dp) 449 | 450 | -------------------------------------------------------------------------------- /fandango/objects.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.5 2 | """ @if gnuheader 3 | ############################################################################# 4 | ## 5 | ## file : objects.py 6 | ## 7 | ## description : see below 8 | ## 9 | ## project : Tango Control System 10 | ## 11 | ## $Author: tcoutinho@cells.es, homs@esrf.fr $ 12 | ## 13 | ## 14 | ## $Revision: 2008 $ 15 | ## 16 | ## copyleft : ALBA Synchrotron Controls Section, CELLS 17 | ## Bellaterra 18 | ## Spain 19 | ## 20 | ############################################################################# 21 | ## 22 | ## This file is part of Tango Control System 23 | ## 24 | ## Tango Control System is free software; you can redistribute it and/or 25 | ## modify it under the terms of the GNU General Public License as published 26 | ## by the Free Software Foundation; either version 3 of the License, or 27 | ## (at your option) any later version. 28 | ## 29 | ## Tango Control System is distributed in the hope that it will be useful, 30 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | ## GNU General Public License for more details. 33 | ## 34 | ## You should have received a copy of the GNU General Public License 35 | ## along with this program; if not, see . 36 | ########################################################################### 37 | @endif 38 | @package object 39 | @description It includes 2 wonderful classes: Object (by ahoms) and Singleton (by MarcSantiago) 40 | @attention THIS MODULE IS DEPRECATED, Use @b tau.core.utils.Singleton and @b tau.core.utils.Object instead! 41 | """ 42 | import __builtin__ 43 | from __builtin__ import object 44 | 45 | from functional import * 46 | from operator import isCallable 47 | import Queue 48 | import functools 49 | 50 | try: from collections import namedtuple #Only available since python 2.6 51 | except: pass 52 | 53 | ## Inspection methods 54 | 55 | def dirModule(module): 56 | return [a for a,v in module.__dict__.items() if getattr(v,'__module__','') == module.__name__] 57 | 58 | def loadModule(source,modulename=None): 59 | #Loads a python module either from source file or from module 60 | from imp import load_source,find_module,load_module 61 | if modulename or '/' in source or '.' in source: 62 | return load_source(modulename or replaceCl('[-\.]','_',source.split('/')[-1].split('.py')[0]),source) 63 | else: 64 | return load_module(source,*find_module(source)) 65 | 66 | def dirClasses(module,owned=False): 67 | v = [a for a,v in module.__dict__.items() if isinstance(v,type)] 68 | if owned: return [a for a in dirModule(module) if a in v] 69 | else: return v 70 | 71 | def obj2dict(obj,type_check=True,class_check=False,fltr=None): 72 | """ Converts a python object to a dictionary with all its members as python primitives 73 | 74 | :param fltr: a callable(name):bool method""" 75 | dct = {} 76 | try: 77 | for name in dir(obj): 78 | if fltr and not fltr(name): 79 | continue 80 | try: 81 | attr = getattr(obj,name) 82 | if hasattr(attr,'__call__'): continue 83 | if name == 'inited_class_list': continue 84 | if name.startswith('__'): continue 85 | if type_check: 86 | try: 87 | if type(attr).__name__ not in dir(__builtin__): 88 | if isinstance(attr,dict): 89 | attr = dict((k,v) for k,v in attr.items()) 90 | else: 91 | attr = str(attr) 92 | except: 93 | continue 94 | dct[name] = attr 95 | except Exception,e: 96 | print(e) 97 | 98 | if class_check: 99 | klass = obj.__class__ 100 | if '__class__' not in dct: dct['__class__'] = klass.__name__ 101 | if '__bases__' not in dct: dct['__bases__'] = [b.__name__ for b in klass.__bases__] 102 | if '__base__' not in dct: dct['__base__'] = klass.__base__.__name__ 103 | except Exception,e: 104 | print(e) 105 | return(dct) 106 | 107 | 108 | ## Useful class objects 109 | 110 | class Struct(object): 111 | """ 112 | Metamorphic class to pass/retrieve data objects as object or dictionary 113 | """ 114 | def __init__(self,*args,**kwargs): 115 | self.load(*args,**kwargs) 116 | def load(self,*args,**kwargs): 117 | dct = args[0] if len(args)==1 else (args or kwargs) 118 | if isSequence(dct) and not isDictionary(dct): dct = dict.fromkeys(dct) #isDictionary also matches items lists 119 | [setattr(self,k,v) for k,v in (dct.items() if hasattr(dct,'items') else dct)] 120 | 121 | def keys(self): return self.__dict__.keys() 122 | def values(self): return self.__dict__.values() 123 | def items(self): return self.__dict__.items() 124 | def __getitem__(self,k): return getattr(self,k) 125 | def __setitem__(self,k): return setattr(self,k,v) 126 | def __contains__(self,k): return hasattr(self,k) 127 | 128 | def __call__(self,*args,**kwargs): 129 | """getter with one string, setter if 2 are passed""" 130 | assert len(args) in (1,2) 131 | if len(args)==2: setattr(self,args[0],args[1]) 132 | elif len(args)==1 and isString(args[0]): return getattr(self,args[0]) 133 | else: self.load(*args,**kwargs) 134 | def __repr__(self): 135 | return 'fandango.Struct({\n'+'\n'.join("\t'%s': %s,"%(k,v) for k,v in self.__dict__.items())+'\n\t})' 136 | def __str__(self): 137 | return self.__repr__().replace('\n','').replace('\t','') 138 | def to_str(self,order=None,sep=','): 139 | """ This method provides a formatable string for sorting""" 140 | return self.__str__() if order is None else (sep.join('%s'%self[k] for k in order)) 141 | 142 | def _fget(self,var): 143 | return getattr(self,var) 144 | def _fset(self,value,var): 145 | setattr(self,var,value) 146 | def _fdel(self,var): 147 | delattr(self,var) 148 | def make_property(var,fget=_fget,fset=_fset,fdel=_fdel): 149 | """ This Class is in Beta, not fully implemented yet""" 150 | return property(partial(fget,var=var),partial(fset,var=var),partial(fdel,var=var),doc='%s property'%var) 151 | 152 | #class NamedProperty(property): 153 | #""" 154 | #""" 155 | #def __init__(self,name,fget=None,fset=None,fdel=None): 156 | #self.name = name 157 | #mname = '%s%s'%(name[0].upper(),name[1:]) 158 | #lname = '%s%s'%(name[0].lower(),name[1:]) 159 | #property.__init__(fget,fset,fdel,doc='NamedProperty(%s)'%self._name) 160 | #def get_attribute_name(self): 161 | #return '_%s'self.name 162 | 163 | def NamedProperty(name,fget=None,fset=None,fdel=None):#,doc=None): 164 | """ 165 | This Class is in Beta, not fully implemented yet 166 | 167 | It makes easier to declare name independent property's (descriptors) by using template methods like: 168 | 169 | def fget(self,var): # var is the identifier of the variable 170 | return getattr(self,var) 171 | def fset(self,value,var): # var is the identifier of the variable 172 | setattr(self,var,value) 173 | def fdel(self,var): # var is the identifier of the variable 174 | delattr(self,var) 175 | 176 | MyObject.X = Property(fget,fset,fdel,'X') 177 | """ 178 | return property(partial(fget,var=name) if fget else None,partial(fset,var=name) if fset else None,partial(fdel,var=name) if fdel else None,doc=name) 179 | 180 | import threading 181 | __lock__ = threading.RLock() 182 | def locked(f,*args,**kwargs): 183 | """ 184 | decorator for secure-locked functions 185 | A key-argument _lock can be used to use a custom Lock object 186 | """ 187 | _lock = kwargs.pop('_lock',__lock__) 188 | try: 189 | _lock.acquire() 190 | return f(*args,**kwargs) 191 | except Exception,e: 192 | print 'Exception in%s(*%s,**%s): %s' % (f.__name__,args,kwargs,e) 193 | finally: 194 | _lock.release() 195 | 196 | def self_locked(func,reentrant=True): 197 | ''' Decorator to make thread-safe class members 198 | @deprecated 199 | @note see in tau.core.utils.containers 200 | Decorator to create thread-safe objects. 201 | reentrant: CRITICAL: 202 | With Lock() this decorator should not be used to decorate nested functions; it will cause Deadlock! 203 | With RLock this problem is avoided ... but you should rely more on python threading. 204 | ''' 205 | @functools.wraps(func) 206 | def lock_fun(self,*args,**kwargs): 207 | #self,args = args[0],args[1:] 208 | if not hasattr(self,'lock'): 209 | setattr(self,'lock',threading.RLock() if reentrant else threading.Lock()) 210 | if not hasattr(self,'trace'): 211 | setattr(self,'trace',False) 212 | self.lock.acquire() 213 | try: 214 | #if self.trace: print "locked: %s"%self.lock 215 | result = func(self,*args,**kwargs) 216 | finally: 217 | self.lock.release() 218 | #if self.trace: print "released: %s"%self.lock 219 | return result 220 | #lock_fun.__name__ = func.__name__ 221 | #lock_fun.__doc__ = func.__doc__ 222 | return lock_fun 223 | 224 | 225 | ############################################################################### 226 | 227 | def NewClass(classname,classparent=None,classdict=None): 228 | """ 229 | Creates a new class on demand: 230 | ReleaseNumber = NewClass('ReleaseNumber',tuple,{'__repr__':(lambda self:'.'.join(('%02d'%i for i in self)))}) 231 | """ 232 | if classparent and not isSequence(classparent): classparent = (classparent,) 233 | return type(classname,classparent or (object,),classdict or {}) 234 | 235 | ############################################################################### 236 | 237 | class Object(object): 238 | """ 239 | This class solves some problems when an object inherits from multiple classes 240 | and some of them inherit from the same 'grandparent' class 241 | """ 242 | 243 | def __init__(self): 244 | """ default initializer 245 | @todo be more clever! 246 | """ 247 | 248 | self.name = None 249 | 250 | pass 251 | ## @var name 252 | # Var does nothing 253 | # @todo be more clever! 254 | 255 | pass 256 | 257 | def call__init__(self, klass, *args, **kw): 258 | if 'inited_class_list' not in self.__dict__: 259 | self.inited_class_list = [] 260 | 261 | if klass not in self.inited_class_list: 262 | self.inited_class_list.append(klass) 263 | #print '%s.call__init__(%s,%s)' % (klass.__name__,args,kw) 264 | klass.__init__(self, *args, **kw) 265 | 266 | def call_all__init__(self, klass, *_args, **_kw): 267 | ''' Call __init__ recursively, for multiple dynamic inheritance. 268 | @author srubio@cells.es 269 | 270 | This method should be called only if all arguments are keywords!!! 271 | Multiple __init__ calls with unnamed arguments is hard to manage: 272 | All the _args values will be assigned to non-keyword args 273 | e.g: 274 | from objects import Object 275 | 276 | class A(Object): 277 | def __init__(self,a=2): 278 | print 'A.__init__',a 279 | class B(A): 280 | def __init__(self,b): 281 | print 'B.__init__',b 282 | class C(B,A): 283 | def __init__(self,c): 284 | print 'C.__init__',c 285 | class D(C,B): 286 | def __init__(self,d=1,*args,**kwargs): 287 | self.call_all__init__(D,*args,**kwargs) 288 | print 'D.__init__',d 289 | D(a=1,b=2,c=3,d=4) 290 | ''' 291 | #if _args: raise Exception,'__init_all_Object_withUnnamedArgumentsException' 292 | from inspect import getargspec 293 | #print '%s.call_all__init__(%s,%s)' % (klass.__name__,_args,_kw) 294 | for base in klass.__bases__: 295 | if 'call__init__' in dir(base) and ('inited_class_list' not in self.__dict__ or base not in self.inited_class_list): 296 | #print '\t%s.base is %s' % (klass.__name__,base.__name__) 297 | nkw,i = {},0 298 | try: 299 | args,largs,kargs,vals = getargspec(base.__init__) 300 | if kargs: nkw = dict(_kw) 301 | for arg in args: 302 | if arg == 'self': continue 303 | if arg in _kw: 304 | nkw[arg] = _kw[arg] 305 | elif i