Index: /fact/tools/PyDimCtrl/fpydim.py
===================================================================
--- /fact/tools/PyDimCtrl/fpydim.py	(revision 13770)
+++ /fact/tools/PyDimCtrl/fpydim.py	(revision 13770)
@@ -0,0 +1,245 @@
+#!/usr/bin/python -tti
+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
+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
+# create one class for each Fact Dim Server
+FactDimServerClasses = []
+for server_name in servers:
+	FactDimServerClasses.append( 
+			types.ClassType( server_name, (FactDimServer,), {}) )
+
+# 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
+all_dims = []
+for i,server_name in enumerate(servers):
+	new_instance = FactDimServerClasses[i](server_name)
+	all_dims.append( new_instance )
+del new_instance
+
+dims = []
+#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)
+
+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)
+
+
+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 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
+
Index: /fact/tools/PyDimCtrl/service.py
===================================================================
--- /fact/tools/PyDimCtrl/service.py	(revision 13770)
+++ /fact/tools/PyDimCtrl/service.py	(revision 13770)
@@ -0,0 +1,263 @@
+#!/usr/bin/python -tt
+	from fpydim import *
+
+def IsReadyForDataTaking( verbose = True ):
+	drive = drive_control
+	bias = bias_control
+	fad = fad_control
+	
+	msg = MSG()
+	msg.output = verbose
+	msg("Checking if System isready for data taking... ")
+
+	ok = True
+	if not drive() == 7:
+		msg.warn( drive.state()[0] + "  NOT ok")
+		ok = False
+	if not feedback() == 12:
+		msg.warn( feedback.state()[0] + "  NOT ok")
+		ok = False
+	if not bias() == 9:
+		msg.warn( bias.state()[0] + "  NOT ok")
+		ok = False
+	if not fad() == 4:
+		msg.warn( fad.state()[0] + "  NOT ok")
+		ok = False
+	
+	if ok:
+		msg.ok( " all ok " )
+
+	return ok
+
+def StopTracking( verbose = True ):
+	msg = MSG()
+	msg.output = verbose
+
+	# defining a shortcut 
+	drive = drive_control
+
+	drive.stop()
+	if not drive.wait(5, 10):
+		msg.warn("drive did not reach state 5 after 10 seconds")
+		msg.warn( drive.state()[0].rstrip('\x00') )
+		return False
+
+	return True
+
+def TakeDataRun( verbose = True ):
+	fad = fad_control	
+	msg = MSG()
+	msg.output = verbose
+	if not IsReadyForDataTaking( verbose=False ):
+		msg.fail("System not Ready for DataTaking")
+		IsReadyForDataTaking( verbose=True )
+		return False
+	mcp.start(300,-1,'data\0')
+	if not fad.wait(8, 10):
+		msg.warn("FAD not in Writing Data after 10seconds")
+		return False
+	msg("... taking data: 300 seconds - normal data run")
+	if not fad.wait(4, 330):
+		msg.warn("FAD did not return to Connected, after 330 seconds")
+		return False
+	return True
+
+def TrackSource( Shift=0.6, Angle=50, SrcName='Crab', verbose=True):
+	drive = drive_control
+	
+	msg = MSG()
+	msg.output = verbose
+
+	if not drive.wait(5, 10):
+		msg.warn("drive not ready after 10sec")
+		msg.warn( drive.state()[0].rstrip('\x00') )
+		return False
+
+	SrcName += '\0'
+	drive.track_source( Shift, Angle, SrcName)
+	
+	return True
+
+def WaitForTracking( CalmDownTime = 30, verbose = True):
+	drive = drive_control
+
+	msg = MSG()
+	msg.output = verbose
+
+	if not drive.wait(6, 10):
+		msg.warn("drive not in state moving after 10sec")
+		return False
+
+	if not drive.wait(7, 10):
+		msg.warn("drive not in state Tracking after 10sec")
+		return False
+
+	msg("waiting "+str(CalmDownTime)\
+			+"sec for drive to calm down and tracking to be stable")
+	time.sleep(CalmDownTime)
+	zoreturn True
+
+def PrepareBias( verbose = True):
+	msg = MSG()
+	msg.output = verbose
+	bias = bias_control
+
+	if feedback() != 12:
+		msg.warn("feedback is not in Current Control")
+		return False
+
+	bias.set_global_dac(1)
+	if not bias.wait(9, 10):
+		msg.warn("bias not in Voltage ON after 10sec")
+		return False
+	if not bias.wait(5, 10):
+		msg.warn("bias not Ramping after 10sec")
+		return False
+	if not bias.wait(9, 30):
+		msg.warn("bias not fully ramped up after 30sec")
+		return False
+
+	msg("waiting 45sec...")	
+	time.sleep(45)
+	return True
+
+def TakePedestalOnRun( verbose = True ):
+	msg = MSG()
+	msg.output = verbose
+	if not IsReadyForDataTaking( verbose=False ):
+		msg.fail("System not Ready for DataTaking")
+		IsReadyForDataTaking( verbose=True )
+		return False
+	mcp.start(-1,1000,'pedestal\0')
+	if not fad.wait(8, 10):
+		msg.warn("FAD not in Writing Data after 10seconds")
+		return False
+	msg("... taking ped: 1000evts @25Hz")
+	if not fad.wait(4, 50):
+		msg.warn("FAD did not return to Connected, after 50 seconds")
+		return False
+	return True
+	
+def TakeExtLpRun( verbose = True ):
+	msg = MSG()
+	msg.output = verbose
+	if not IsReadyForDataTaking( verbose=False ):
+		msg.fail("System not Ready for DataTaking")
+		IsReadyForDataTaking( verbose=True )
+		return False
+	mcp.start(-1,1000,'light-pulser-ext\0')
+	if not fad.wait(8, 10):
+		msg.warn("FAD not in Writing Data after 10seconds")
+		return False
+	msg("... taking light-pulser-ext: 1000evts @25Hz")
+	if not fad.wait(4, 50):
+		msg.warn("FAD did not return to Connected, after 50 seconds")
+		return False
+	zoreturn True
+
+def TakeData( verbose = True):
+	msg = MSG()
+	msg.output = verbose
+	TakePedestalOnRun()
+	TakeExtLpRun()
+	for i in range(4):
+		i +=1
+		msg("Taking Data Run "+str(i)+" of 4")
+		TakeDataRun()
+
+def Take( time=0, events=0, runtype='drs-pedestal', verbose=True):
+	msg = MSG()
+	msg.output = verbose
+	fad = fad_control
+	runtype += '\0'
+	if not fad.wait(4, 10):
+		msg.warn("fad not connected after 10sec")
+		return False
+	mcp.start( time, events, runtype)
+	if not fad.wait(8, 30):
+		msg.warn("fad not Writing Data after 30sec")
+		return False
+	timeout = float('inf')
+	if time != -1:
+		timeout = time*1.1
+	if not fad.wait(4, timeout):
+		msg.warn("Data Writing not finished after "+str(timeout)+"sec")
+		return False
+	return True
+
+def TakeDrsCalibration( verbose = True):
+	msg = MSG()
+	msg.output = verbose
+	bias = bias_control
+	fad = fad_control
+
+	bias.set_zero_voltage()
+	if not bias.wait(7, 10):
+		msg.warn("bias has not switched of after 10sec")
+		return False
+	fad.start_drs_calibration()
+	Take( -1, 1000, 'drs-pedestal')
+	Take( -1, 1000, 'drs-gain')
+	Take( -1, 1000, 'drs-pedestal')
+	fad.set_file_format(2)
+	Take( -1, 1000, 'drs-pedestal')
+	Take( -1, 1000, 'drs-time')
+	Take( -1, 1000, 'drs-time-upshifted')
+	fad.reset_secondary_drs_baseline()
+	Take(-1, 1000, 'pedestal')
+	fad.set_file_format(2)
+	Take(-1, 1000, 'pedestal')
+
+def BlinkenLights(verbose = True):
+	msg = MSG(verbose)
+	fad = fad_control
+
+	for i in range(10):
+		fad.set_file_format(2)
+		time.sleep(1)
+		fad.set_file_format(0)
+		time.sleep(1)
+
+def TakeAmplCalib( roi=1024, verbose=True):
+	msg = MSG(verbose)
+	bias = bias_control
+	fad  = fad_control
+	if (roi!=1024) and (roi!=300):
+		raise ValueError('roi must be 300 or 1024')
+	# I do not understand, why the out put needs to be ENABLED??
+	feedback.enable_output(0)
+	bias.set_zero_voltage()
+	bias.wait(7) # VoltageOff
+	fad.start_drs_calibration
+	
+	Take(-1, 1000, 'drs-pedestal')
+	Take(-1, 1000, 'drs-gain')
+	if roi == 300:
+		Take(-1, 1000, 'pedestal')
+	if roi == 1024:
+		Take(-1, 1000, 'drs-pedestal')
+
+	fad.set_file_format(2)
+	if roi == 300:
+		Take(-1, 1000, 'pedestal')
+	if roi == 1024:
+		Take(-1, 1000, 'drs-pedestal')
+
+def TakeTimeCalib( verbose=True ):
+	msg = MSG( verbose )
+	feedback.enable_output(0)
+	if bias() != 7:
+		bias.set_zero_voltage()
+		if not bias.wait(7, 10):
+			msg.warn("bias not ramped down after 10sec")
+			return False
+	fad.set_file_format(2)
+	Take(-1, 1000, 'drs-time')
+	Take(-1, 1000, 'drs-time-upshifted')
+
+if __name__ == '__main__':
+	IsReadyForDataTaking()
+	TrackSource( Shift=0.6, Angle=50, SrcName='Crab', verbose=True)
+	WaitForTracking( CalmDownTime = 30, verbose = True)
+	TakeDataRun()
+	StopTracking()
