#!/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') pydim.dic_set_dns_node('newdaq') def print_command( name, f ): meth_name = name.split('/')[1].lower() if iskeyword(meth_name): meth_name += '_cmd' if name in dd: if not dd[name]: doc_string = "DESC in SERVICE_DESC is empty ?!" else: doc_string = dd[name] else: doc_string = "-- no DESC found in SERVICE_DESC --" doc_string += '\n' doc_string += services[name.split('/')[0]][name][0] # this is the new command, it simply calls the _cmd() method f.write(" def " + meth_name + "(self, *args):\n" ) f.write(' """ ' + doc_string +'\n' ) f.write(' """ \n') f.write(' self._cmd("' + meth_name.upper() + '", *args)\n' ) def print_getter( name, f ): meth_name = name.split('/')[1].lower() if iskeyword(meth_name): meth_name += '_get' if name in dd: if not dd[name]: doc_string = "DESC in SERVICE_DESC is empty ?!" else: doc_string = dd[name] else: doc_string = "-- no DESC found in SERVICE_DESC --" doc_string += '\n' doc_string += services[name.split('/')[0]][name][0] # this is the new command, it simply calls the _cmd() method f.write(" def " + meth_name + "(self):\n" ) f.write(' """ ' + doc_string+ '\n') f.write(' """ \n') f.write(' return self._get("' + meth_name.upper() + '")\n' ) 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=1) 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() #print 'full_srv_name',full_srv_name #print 'desc', desc return pydim.dic_sync_info_service(full_srv_name, desc, timeout=1) 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() # 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( timeout = 5): """ requests DIS_DNS/SERVER_LSIT and creates dict *servers* from it, which is returned as well. the returned dict contains: { "SERVER_NAME": (pid_of_server_process, host_the_server_runs_on) } the SERVER_NAME seems to be in capital letter all the time, but I'm not sure if this is really ensured by Dim or if this was ensured by Thomas manually. So maybe one should not rely on this. In case the request of DIS_DNS/SERVER_LIST times out, no Exception is thrown, but the program exits. This is probably bullshit. """ # making server list rawlist = pydim.dic_sync_info_service('DIS_DNS/SERVER_LIST','C', timeout=5) if rawlist == None: ## todo: throw an exception here! print "couldn't get the server list of DIS_DNS. program abortion..." sys.exit(1) #print rawlist # 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 (ascii-zero) # 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 dict-values are not needed, so it might be any iteratable python listlike type # to be precise def ParseServersServiceList( servers ): """ this function parses SERVER_NAME/SERVICE_LIST as well as SERVER_NAME/SERVICE_DESC of all SERVER_NAME in the input list-like *servers* two dicts are returned: *services* - nested dict: { "SERVER_NAME" : { "SERVER_NAME/SERVICE_NAME" : ( "format-description-string like: I:2;C", "" or "CMP" or "RPC", "service description text" ) } } *dd* - normal dict: { "SERVER_NAME/SERVICE_NAME" : "service description text" } """ services = {} dd = {} for server in servers: # sl_raw is a tuple, with one really long string, which needs to be parsed sl_raw = pydim.dic_sync_info_service(server+'/SERVICE_LIST','C',timeout=3)[0] if sl_raw == None: print "couldn't get the service list of ", server, "program abortion..." sys.exit(1) # print server # print sl_raw # 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',timeout=3)[0] if sd_raw == None: print "couldn't get the service description list of ", server, "program abortion..." sys.exit(1) 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 not '/' in service: service = server+'/'+service #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 if __name__ == '__main__': print 'requesting and parsing DIM DNS server list' servers = ParseDnsServerList() print '... parsing each servers service list' services, dd = ParseServersServiceList( servers ) # since I want this text to created for reference # I will create it here print "creating fix class definitions in a file:" f = open("fact_dim_servers.py", "w") f.write("from make_fix_dimservers import FactDimServer\n") for server_name in servers: if server_name == "DIS_DNS": continue f.write("class "+server_name+" ( FactDimServer): \n") for cmd in services[server_name]: cmdname = cmd.split('/') if len(cmdname) > 1: cmdname = cmdname[1] elif len(cmdname) >0: cmdname = cmdname[0] cmd = server_name+'/'+cmdname else: print server_name, cmd raise ValueError('was not able to parse service/command names') if 'CMD' in services[server_name][cmd][1]: print_command(cmd, f) elif not services[server_name][cmd][1]: print_getter(cmd, f) f.close() import fact_dim_servers # 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 print 'creating an instance of each FACT DIM server' dims = [] new_instance = None for server_name in servers: if server_name == 'DIS_DNS': continue new_instance = getattr(sys.modules["fact_dim_servers"], server_name)(server_name, services) dims.append( new_instance ) globals()[server_name.lower()] = new_instance del new_instance print '.... fact DIM servers are ready to use' print '-'*70 print