#!/usr/bin/python -tti # for tab completion import rlcompleter import readline readline.parse_and_bind('tab: complete') import sys # for sys.exit() during bebugging mostly :-) import types # for dynamic class construction import time # for time.sleep from pprint import pprint # for nice printing from keyword import iskeyword # in case dynamic methods are equal to keywords import pydim # for the C-API Dim Call Wrappers # using this line makes 'export DIM_DNS_NODE=daq' obsolete pydim.dic_set_dns_node('daq') class FactDimServer( object ): def __init__(self, name): """ sets name of instance to name of server, all uppercase """ self.list_of_states = [] self.name = name.upper() self.print_state = False self.print_msg = False self.reg_state_cb() self.reg_msg_cb() self.user_func = None self.__delay_between_cmds = 1.0 self.__delay_between_services = 1.0 self.__last_cmd_send = -1*float('inf') self.__last_service_got = -1*float('inf') def _cmd(self, cmdstr, *args): """ used by all dynamicly created methods, which call a Dim CMD """ cmdstr=self.name+'/'+cmdstr.upper() desc = services[self.name][cmdstr.upper()][0] # there is a work around for a bug in pydim # even if a command needs no argument, and desc is also empty string # one has to give one ... need to tell Niko about it. if not desc: desc = 'I' args=(1,) elif desc == 'O': args = (0,) while not time.time() - self.__last_cmd_send > self.__delay_between_cmds: time.sleep(0.5) self.__last_cmd_send = time.time() pydim.dic_sync_cmnd_service(cmdstr, args, desc, timeout=None) def _get(self, service): """ used by all dynamicly created methods, which get a service """ full_srv_name = self.name+'/'+service.upper() desc = services[self.name][full_srv_name][0] while not time.time() - self.__last_service_got > self.__delay_between_services: time.sleep(0.5) self.__last_service_got = time.time() return pydim.dic_sync_info_service(full_srv_name, desc) def __call__(self): """ Wrapper / For Convenience self.state() returns a string (if it exists) *returns* numeric state code, parsed from return of self.state() """ if hasattr(self, 'stn'): return self.stn else: raise TypeError(self.name+' has no CMD called STATE') def wait(self, state_num, timeout=None): """ waits for a certain state BLOCKING returns True if state was reached returns False if timeout occured raises TypeError if Server has no method state """ if not hasattr(self, 'stn'): raise TypeError(self.name+' has no CMD called STATE') if timeout == None: timeout = float('inf') else: timeout = float(timeout) start = time.time() while not self.stn == state_num: time.sleep(0.1) if time.time() >= start+timeout: return False return True def state_callback(self, state): self.sts = state try: self.stn = int(state[state.find('[')+1 : state.find(']')]) except ValueError: self.stn = None self.last_st_change = time.time() self.list_of_states.append( (self.last_st_change, self.stn) ) if len(self.list_of_states) > 10000: print "list_of_states too long, truncating..." self.list_of_states = self.list_of_states[1000:] if self.user_func: self.user_func( self.stn ) if self.print_state: print state def msg_callback(self, msg): if self.print_msg: print msg def reg_state_cb(self): if not hasattr(self, 'state'): raise TypeError(self.name+' has no CMD called STATE') service_name = self.name.upper()+'/STATE' self.state_sid = pydim.dic_info_service(service_name, "C", self.state_callback) if not self.state_sid: del self.state_sid raise IOError('could not register STATE client') def reg_msg_cb(self): if not hasattr(self, 'state'): raise TypeError(self.name+' has no CMD called STATE') service_name = self.name.upper()+'/MESSAGE' self.msg_sid = pydim.dic_info_service(service_name, "C", self.msg_callback) if not self.msg_sid: del self.msg_sid raise IOError('could not register MESSAGE client') def unreg_state_cb(self): if hasattr(self, 'state_sid'): pydim.dic_release_service(self.state_sid) del self.state_sid def unreg_msg_cb(self): if hasattr(self, 'msg_sid'): pydim.dic_release_service(self.msg_sid) del self.msg_sid def __del__(self): self.unreg_state_cb() self.unreg_msg_cb() # utility functions for dynamic addid of methods to classes def add_command(cls, name): meth_name = name.split('/')[1].lower() if iskeyword(meth_name): meth_name += '_cmd' # this is the new command, it simple calls the _cmd() method def new_command(self, *args): self._cmd(meth_name, *args) new_command.__name__ = meth_name # from this line on, the docstring of the method is created if name in dd: if not dd[name]: new_command.__doc__ = "DESC in SERVICE_DESC is empty ?!" else: new_command.__doc__ = dd[name] else: new_command.__doc__ = "-- no DESC found in SERVICE_DESC --" new_command.__doc__ += '\n' new_command.__doc__ += services[name.split('/')[0]][name][0] # this line make the new_command() method, a method of the class cls # giving it the name new_command.__name__ setattr( cls, new_command.__name__, new_command) # add_getter is very similar to add_command, # the only difference is, that it calls _get() instead of _cmd() # and since _get() has a return value, this return value is vorwarded to the user def add_getter(cls, name): meth_name = name.split('/')[1].lower() if iskeyword(meth_name): meth_name += '_cmd' def new_command(self): return self._get(meth_name) new_command.__name__ = meth_name if name in dd: if not dd[name]: new_command.__doc__ = "DESC in SERVICE_DESC is empty ?!" else: new_command.__doc__ = dd[name] else: new_command.__doc__ = "-- no DESC found in SERVICE_DESC --" new_command.__doc__ += '\n' new_command.__doc__ += services[name.split('/')[0]][name][0] setattr( cls, new_command.__name__, new_command) # In order to create classes according to the Dim-Servers, currently connected # to the DIS_DNS I have to parse DIS_DNS/SERVER_LIST # This is done in two steps, first I get the list of Server Names from DIS_DNS # and the I get the list of each servers services and cmds, # from each servers SERVICE_LIST and the service/command description # from each servers SERVICE_DESC # I get quite a lot of information, which I store in python dicts, or # even nested dicts, if necessary. def ParseDnsServerList(): # making server list rawlist = pydim.dic_sync_info_service('DIS_DNS/SERVER_LIST','C') # the output needs to be treated a bit .. it is a tuple with only one long string in it # the string contains | and the strange character \x00 # I use both to cut the list apart rawlist = rawlist[0].split('\x00') servers_n_hosts = rawlist[0].split('|') server_ids = rawlist[1].split('|') servers = {} for i,snh in enumerate(servers_n_hosts): snh = snh.split('@') s = snh[0] h = snh[1] sid = server_ids[i] servers[s] = (sid, h) return servers # servers should be a dict containing uppercase server names as keys, # the values are not needed, so it might be any iteratable python listlike type # to be precise def ParseServersServiceList( servers ): services = {} dd = {} for server in servers: # sl_raw is a tuple, with an really long string, which needs to be parsed sl_raw = pydim.dic_sync_info_service(server+'/SERVICE_LIST','C')[0] # even without parsing, I can find out, if this server also gives me a # service description list. In case it does not, this is fine as well # the doc string of the dynamicly created methods, will then contain # a note, that therer was no SERVICE_DESC ... if server+'/SERVICE_DESC' in sl_raw: sd_raw = pydim.dic_sync_info_service(server+'/SERVICE_DESC','C')[0] else: sd_raw = '' # now before parsing, I strip off all ASCII zeroes '\x00' and all # line breaks off the *end* of the long string of both # the service list sl # and service description sd # # I think in sl_raw were alse some '\x00' in the middle .. these # are replaced by nothing, in case they are there. sl_raw = sl_raw.rstrip('\x00\n') sl_raw = sl_raw.replace('\x00','') sd_raw = sd_raw.rstrip('\x00\n') # The lists are seperated by line breaks, so I split them using this sl = sl_raw.split('\n') sd = sd_raw.split('\n') # First I parse the service descriptons, so I have them available, # when I create the dict full of services. # All desciptions look like this # 'SERVER/SERVICE=some descriptive text' or # 'SERVER/SERVICE=' # this I use to create the dictionary. # create descripton dict dd from service_desc list sd for d_str in sd: service,equalsign,desc = d_str.partition('=') #if '=' != equalsign: # print "Error: server:", server, "desc:", d_str dd[service] = desc # Now I fill ther services dict. Each server gets a nested dict # inside services. # Each service is explained in a string with a '|' in between. # I use this for spliting. # The string look like this # SERVER/SERVICE|format-desc-str(e.g. I:2;C)|-empty- or CMD or RPC| services[server] = {} for service in sl: service = service.split('|') if service[0] in dd: services[server][service[0]] = ( service[1], service[2], dd[service[0]]) return services, dd servers = ParseDnsServerList() services, dd = ParseServersServiceList( servers ) # create one class for each Fact Dim Server FactDimServerClasses = [] for server_name in servers: FactDimServerClasses.append( types.ClassType( server_name, (FactDimServer,), {}) ) for cmd in services[server_name]: if 'CMD' in services[server_name][cmd][1]: cmdname = cmd.split('/')[1] add_command(FactDimServerClasses[-1], cmd) elif not services[server_name][cmd][1]: cmdname = cmd.split('/')[1] add_getter(FactDimServerClasses[-1], cmd) # create an instace of each of the classes # and make it globally known, i.e. known to the Python interpreter # all the ServerClass instances are collected in a list # so one can get a quick overview --> print dims dims = [] for i,server_name in enumerate(servers): if server_name == 'DIS_DNS': continue new_instance = FactDimServerClasses[i](server_name) dims.append( new_instance ) globals()[server_name.lower()] = new_instance del new_instance del i ############################################################################## # class for colored printing class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' def disable(self): self.HEADER = '' self.OKBLUE = '' self.OKGREEN = '' self.WARNING = '' self.FAIL = '' self.ENDC = '' ############################################################################## # class which implements colored printing # method calls can be used instead of Python print calls # for conveniently printing colored output. class MSG( bcolors): def __init__(self, verbose = True): """ create MSG instance, default is verbose, sets self.output if: self.*output* = True, object behaves as expeted if False, no call return anything """ self.output = verbose def fail(self, text ): """ print in RED """ text = str(text) if self.output: print bcolors.FAIL + "ERROR:" + bcolors.ENDC, print bcolors.FAIL + text + bcolors.ENDC def warn(self, text ): """ print in YELLOW """ text = str(text) if self.output: print bcolors.WARNING + text + bcolors.ENDC def ok(self, text ): """ print in GREEN """ text = str(text) if self.output: print bcolors.OKGREEN + text + bcolors.ENDC def __call__(self, *args): """ print as Python print would do """ if self.output: for arg in args: print arg, print