Index: fact/tools/PyDimCtrl/fpydim.py
===================================================================
--- fact/tools/PyDimCtrl/fpydim.py	(revision 13770)
+++ fact/tools/PyDimCtrl/fpydim.py	(revision 13789)
@@ -1,139 +1,239 @@
 #!/usr/bin/python -tti
+
+# for tab completion
 import rlcompleter
 import readline
 readline.parse_and_bind('tab: complete')
 
-import sys
-import pydim
-import types
-import time # for sleep
-from pprint import pprint
-from keyword import iskeyword
+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')
 
-# 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)
-
-# delete unneeded vars
-del servers_n_hosts
-del rawlist
-del server_ids
-
-# print servers
-
-# make service list
-services = {}
-dd = {}
-for serv in servers.keys():
-	# print serv+'/SERVICE_LIST'
-	sl_raw = pydim.dic_sync_info_service(serv+'/SERVICE_LIST','C')[0]
-	if serv+'/SERVICE_DESC' in sl_raw:
-		sd_raw = pydim.dic_sync_info_service(serv+'/SERVICE_DESC','C')[0]
-	else:
-		# print "Warning:", serv+'/SERVICE_DESC' , "not in SERVICE_LIST of", serv
-		sd_raw = ''
-	sl_raw = sl_raw.rstrip('\x00\n')
-	sl_raw = sl_raw.replace('\x00','')
-	sd_raw = sd_raw.rstrip('\x00\n')
-	sl = sl_raw.split('\n')
-	sd = sd_raw.split('\n')
-	
-	# 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:", serv, "desc:", d_str
-		dd[service] = desc
-
-
-	services[serv] = {}
-	for service in sl:
-		service = service.split('|')
-		if service[0] in dd:
-			services[serv][service[0]] = (
-					service[1], service[2], dd[service[0]])
-	 #	else:
-			# print service[0], 'has no DESC in SERVICE_DESC'
-
-# pprint( services )
-
 class FactDimServer( object ):
 
-	def __init__(self, name):
-		self.name = name
-
-	def cmd(self, cmdstr, *args):
-		#args = cmdstr.split(' ')
-		#for i in range(len(args)):
-	#		i = len(args)-1-i
-	#		if not args[i]:
-	#			del args[i]
-		cmdstr=self.name+'/'+cmdstr.upper()
-	#	args = tuple(args[1:])
-		desc = services[self.name][cmdstr.upper()][0]
-		# there is a bug in the pydim library ... 
-		# even if a command need no argument
-		# one has to give one ... need to tell Niko about it.
-		if len(desc) == 0:
-			desc = 'I'
-			args=(1,)
-		# print 'cmd: cmdstr:', cmdstr,
-		# print '-args:', args,
-		# print '-desc:', desc
-		pydim.dic_sync_cmnd_service(cmdstr, args, desc, timeout=None)
-	def get(self, service):
-		full_srv_name = self.name+'/'+service.upper()
-		desc = services[self.name][full_srv_name][0]
-		return pydim.dic_sync_info_service(full_srv_name, desc)
-
-	def __call__(self):
-		""" get Server State as numeric code
-		    for convenience
-		"""
-		if hasattr(self, 'state'):
-			s = self.state()[0]
-			return int(s[ s.find('[')+1 : s.find(']') ])
-		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, 'state'):
-			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() == state_num:
-			time.sleep(0.1)
-			if time.time() >= start+timeout:
-				return False
-		return True
+    def __init__(self, name):
+        """ sets name of instance to name of server, all uppercase
+        """
+        self.name = name.upper()
+
+    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,)
+        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]
+        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, 'state'):
+            s = self.state()[0]
+            return int(s[ s.find('[')+1 : s.find(']') ])
+        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, 'state'):
+            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() == state_num:
+            time.sleep(0.1)
+            if time.time() >= start+timeout:
+                return False
+        return True
+
+
+
+# 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
+
+
+servers = ParseDnsServerList()
+services = ParseServersServiceList( servers )
+
 # create one class for each Fact Dim Server
 FactDimServerClasses = []
 for server_name in servers:
-	FactDimServerClasses.append( 
-			types.ClassType( server_name, (FactDimServer,), {}) )
+    FactDimServerClasses.append( 
+            types.ClassType( server_name, (FactDimServer,), {}) )
 
 # create an instace of each of the classes
@@ -143,6 +243,6 @@
 all_dims = []
 for i,server_name in enumerate(servers):
-	new_instance = FactDimServerClasses[i](server_name)
-	all_dims.append( new_instance )
+    new_instance = FactDimServerClasses[i](server_name)
+    all_dims.append( new_instance )
 del new_instance
 
@@ -150,96 +250,84 @@
 #print "connecting to Dim Servers... "
 for dim in all_dims:
-#	print dim.name.lower()
-	if dim.name == 'DIS_DNS':
-		continue
-	globals()[dim.name.lower()] = dim
-	dims.append(dim)
-
-# utility class 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'
-	def new_command(self, *args):
-		self.cmd(meth_name, *args)
-	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)
-
-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)
+#    print dim.name.lower()
+    if dim.name == 'DIS_DNS':
+        continue
+    globals()[dim.name.lower()] = dim
+    dims.append(dim)
 
 for i,dim in enumerate(all_dims):
-	for cmd in services[dim.name]:
-		if 'CMD' in services[dim.name][cmd][1]:
-			cmdname = cmd.split('/')[1]
-			add_command(FactDimServerClasses[i], cmd)
-		elif not services[dim.name][cmd][1]:
-			cmdname = cmd.split('/')[1]
-			add_getter(FactDimServerClasses[i], cmd)
-
+    for cmd in services[dim.name]:
+        if 'CMD' in services[dim.name][cmd][1]:
+            cmdname = cmd.split('/')[1]
+            add_command(FactDimServerClasses[i], cmd)
+        elif not services[dim.name][cmd][1]:
+            cmdname = cmd.split('/')[1]
+            add_getter(FactDimServerClasses[i], cmd)
+
+##############################################################################
+# 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 = ''
+        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):
-		self.output = verbose
-	
-	def fail(self, text ):
-		text = str(text)
-		if self.output:
-			print bcolors.FAIL + "ERROR:" + bcolors.ENDC,
-			print bcolors.FAIL + text + bcolors.ENDC
-
-	def warn(self, text ):
-		text = str(text)
-		if self.output:
-			print bcolors.WARNING + text + bcolors.ENDC
-
-	def ok(self, text ):
-		text = str(text)
-		if self.output:
-			print bcolors.OKGREEN + text + bcolors.ENDC
-
-	def __call__(self, *args):
-		if self.output:
-			for arg in args:
-				print arg,
-			print
-
+    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
+
