source: fact/tools/PyDimCtrl/fpydim.py@ 13789

Last change on this file since 13789 was 13789, checked in by neise, 12 years ago
more comments and unimportant stuff
  • Property svn:executable set to *
File size: 11.1 KB
Line 
1#!/usr/bin/python -tti
2
3# for tab completion
4import rlcompleter
5import readline
6readline.parse_and_bind('tab: complete')
7
8import sys # for sys.exit() during bebugging mostly :-)
9import types # for dynamic class construction
10import time # for time.sleep
11from pprint import pprint # for nice printing
12
13from keyword import iskeyword # in case dynamic methods are equal to keywords
14
15import pydim # for the C-API Dim Call Wrappers
16
17# using this line makes 'export DIM_DNS_NODE=daq' obsolete
18pydim.dic_set_dns_node('daq')
19
20class FactDimServer( object ):
21
22 def __init__(self, name):
23 """ sets name of instance to name of server, all uppercase
24 """
25 self.name = name.upper()
26
27 def _cmd(self, cmdstr, *args):
28 """ used by all dynamicly created methods, which call a Dim CMD
29 """
30 cmdstr=self.name+'/'+cmdstr.upper()
31 desc = services[self.name][cmdstr.upper()][0]
32
33 # there is a work around for a bug in pydim
34 # even if a command needs no argument, and desc is also empty string
35 # one has to give one ... need to tell Niko about it.
36 if not desc:
37 desc = 'I'
38 args=(1,)
39 pydim.dic_sync_cmnd_service(cmdstr, args, desc, timeout=None)
40
41
42 def _get(self, service):
43 """ used by all dynamicly created methods, which get a service
44 """
45 full_srv_name = self.name+'/'+service.upper()
46 desc = services[self.name][full_srv_name][0]
47 return pydim.dic_sync_info_service(full_srv_name, desc)
48
49
50 def __call__(self):
51 """ Wrapper / For Convenience
52 self.state() returns a string (if it exists)
53 *returns* numeric state code, parsed from return of self.state()
54 """
55 if hasattr(self, 'state'):
56 s = self.state()[0]
57 return int(s[ s.find('[')+1 : s.find(']') ])
58 else:
59 raise TypeError(self.name+' has no CMD called STATE')
60
61 def wait(self, state_num, timeout=None):
62 """ waits for a certain state
63 BLOCKING
64 returns True if state was reached
65 returns False if timeout occured
66 raises TypeError if Server has no method state
67 """
68
69 if not hasattr(self, 'state'):
70 raise TypeError(self.name+' has no CMD called STATE')
71 if timeout == None:
72 timeout = float('inf')
73 else:
74 timeout = float(timeout)
75 start = time.time()
76 while not self() == state_num:
77 time.sleep(0.1)
78 if time.time() >= start+timeout:
79 return False
80 return True
81
82
83
84# utility functions for dynamic addid of methods to classes
85def add_command(cls, name):
86 meth_name = name.split('/')[1].lower()
87 if iskeyword(meth_name):
88 meth_name += '_cmd'
89
90 # this is the new command, it simple calls the _cmd() method
91 def new_command(self, *args):
92 self._cmd(meth_name, *args)
93
94 new_command.__name__ = meth_name
95
96 # from this line on, the docstring of the method is created
97 if name in dd:
98 if not dd[name]:
99 new_command.__doc__ = "DESC in SERVICE_DESC is empty ?!"
100 else:
101 new_command.__doc__ = dd[name]
102 else:
103 new_command.__doc__ = "-- no DESC found in SERVICE_DESC --"
104 new_command.__doc__ += '\n'
105 new_command.__doc__ += services[name.split('/')[0]][name][0]
106
107 # this line make the new_command() method, a method of the class cls
108 # giving it the name new_command.__name__
109 setattr( cls, new_command.__name__, new_command)
110
111# add_getter is very similar to add_command,
112# the only difference is, that it calls _get() instead of _cmd()
113# and since _get() has a return value, this return value is vorwarded to the user
114def add_getter(cls, name):
115 meth_name = name.split('/')[1].lower()
116 if iskeyword(meth_name):
117 meth_name += '_cmd'
118 def new_command(self):
119 return self._get(meth_name)
120 new_command.__name__ = meth_name
121 if name in dd:
122 if not dd[name]:
123 new_command.__doc__ = "DESC in SERVICE_DESC is empty ?!"
124 else:
125 new_command.__doc__ = dd[name]
126 else:
127 new_command.__doc__ = "-- no DESC found in SERVICE_DESC --"
128 new_command.__doc__ += '\n'
129 new_command.__doc__ += services[name.split('/')[0]][name][0]
130 setattr( cls, new_command.__name__, new_command)
131
132
133
134
135
136# In order to create classes according to the Dim-Servers, currently connected
137# to the DIS_DNS I have to parse DIS_DNS/SERVER_LIST
138# This is done in two steps, first I get the list of Server Names from DIS_DNS
139# and the I get the list of each servers services and cmds,
140# from each servers SERVICE_LIST and the service/command description
141# from each servers SERVICE_DESC
142# I get quite a lot of information, which I store in python dicts, or
143# even nested dicts, if necessary.
144
145def ParseDnsServerList():
146 # making server list
147 rawlist = pydim.dic_sync_info_service('DIS_DNS/SERVER_LIST','C')
148 # the output needs to be treated a bit .. it is a tuple with only one long string in it
149 # the string contains | and the strange character \x00
150 # I use both to cut the list apart
151 rawlist = rawlist[0].split('\x00')
152 servers_n_hosts = rawlist[0].split('|')
153 server_ids = rawlist[1].split('|')
154
155 servers = {}
156 for i,snh in enumerate(servers_n_hosts):
157 snh = snh.split('@')
158 s = snh[0]
159 h = snh[1]
160 sid = server_ids[i]
161 servers[s] = (sid, h)
162
163 return servers
164
165
166
167# servers should be a dict containing uppercase server names as keys,
168# the values are not needed, so it might be any iteratable python listlike type
169# to be precise
170def ParseServersServiceList( servers )
171
172 services = {}
173 dd = {}
174 for server in servers:
175 # sl_raw is a tuple, with an really long string, which needs to be parsed
176 sl_raw = pydim.dic_sync_info_service(server+'/SERVICE_LIST','C')[0]
177
178 # even without parsing, I can find out, if this server also gives me a
179 # service description list. In case it does not, this is fine as well
180 # the doc string of the dynamicly created methods, will then contain
181 # a note, that therer was no SERVICE_DESC ...
182 if server+'/SERVICE_DESC' in sl_raw:
183 sd_raw = pydim.dic_sync_info_service(server+'/SERVICE_DESC','C')[0]
184 else:
185 sd_raw = ''
186
187 # now before parsing, I strip off all ASCII zeroes '\x00' and all
188 # line breaks off the *end* of the long string of both
189 # the service list sl
190 # and service description sd
191 #
192 # I think in sl_raw were alse some '\x00' in the middle .. these
193 # are replaced by nothing, in case they are there.
194 sl_raw = sl_raw.rstrip('\x00\n')
195 sl_raw = sl_raw.replace('\x00','')
196 sd_raw = sd_raw.rstrip('\x00\n')
197
198 # The lists are seperated by line breaks, so I split them using this
199 sl = sl_raw.split('\n')
200 sd = sd_raw.split('\n')
201
202 # First I parse the service descriptons, so I have them available,
203 # when I create the dict full of services.
204 # All desciptions look like this
205 # 'SERVER/SERVICE=some descriptive text' or
206 # 'SERVER/SERVICE='
207 # this I use to create the dictionary.
208 # create descripton dict dd from service_desc list sd
209 for d_str in sd:
210 service,equalsign,desc = d_str.partition('=')
211 #if '=' != equalsign:
212 # print "Error: server:", server, "desc:", d_str
213 dd[service] = desc
214
215 # Now I fill ther services dict. Each server gets a nested dict
216 # inside services.
217 # Each service is explained in a string with a '|' in between.
218 # I use this for spliting.
219 # The string look like this
220 # SERVER/SERVICE|format-desc-str(e.g. I:2;C)|-empty- or CMD or RPC|
221 services[server] = {}
222 for service in sl:
223 service = service.split('|')
224 if service[0] in dd:
225 services[server][service[0]] = (
226 service[1], service[2], dd[service[0]])
227 return services
228
229
230servers = ParseDnsServerList()
231services = ParseServersServiceList( servers )
232
233# create one class for each Fact Dim Server
234FactDimServerClasses = []
235for server_name in servers:
236 FactDimServerClasses.append(
237 types.ClassType( server_name, (FactDimServer,), {}) )
238
239# create an instace of each of the classes
240# and make it globally known, i.e. known to the Python interpreter
241# all the ServerClass instances are collected in a list
242# so one can get a quick overview --> print dims
243all_dims = []
244for i,server_name in enumerate(servers):
245 new_instance = FactDimServerClasses[i](server_name)
246 all_dims.append( new_instance )
247del new_instance
248
249dims = []
250#print "connecting to Dim Servers... "
251for dim in all_dims:
252# print dim.name.lower()
253 if dim.name == 'DIS_DNS':
254 continue
255 globals()[dim.name.lower()] = dim
256 dims.append(dim)
257
258for i,dim in enumerate(all_dims):
259 for cmd in services[dim.name]:
260 if 'CMD' in services[dim.name][cmd][1]:
261 cmdname = cmd.split('/')[1]
262 add_command(FactDimServerClasses[i], cmd)
263 elif not services[dim.name][cmd][1]:
264 cmdname = cmd.split('/')[1]
265 add_getter(FactDimServerClasses[i], cmd)
266
267##############################################################################
268# class for colored printing
269
270class bcolors:
271 HEADER = '\033[95m'
272 OKBLUE = '\033[94m'
273 OKGREEN = '\033[92m'
274 WARNING = '\033[93m'
275 FAIL = '\033[91m'
276 ENDC = '\033[0m'
277
278 def disable(self):
279 self.HEADER = ''
280 self.OKBLUE = ''
281 self.OKGREEN = ''
282 self.WARNING = ''
283 self.FAIL = ''
284 self.ENDC = ''
285
286##############################################################################
287# class which implements colored printing
288# method calls can be used instead of Python print calls
289# for conveniently printing colored output.
290
291
292class MSG( bcolors):
293 def __init__(self, verbose = True):
294 """ create MSG instance,
295 default is verbose,
296 sets self.output
297
298 if:
299 self.*output* = True, object behaves as expeted
300 if False, no call return anything
301 """
302 self.output = verbose
303
304 def fail(self, text ):
305 """ print in RED
306 """
307 text = str(text)
308 if self.output:
309 print bcolors.FAIL + "ERROR:" + bcolors.ENDC,
310 print bcolors.FAIL + text + bcolors.ENDC
311
312 def warn(self, text ):
313 """ print in YELLOW
314 """
315 text = str(text)
316 if self.output:
317 print bcolors.WARNING + text + bcolors.ENDC
318
319 def ok(self, text ):
320 """ print in GREEN
321 """
322 text = str(text)
323 if self.output:
324 print bcolors.OKGREEN + text + bcolors.ENDC
325
326 def __call__(self, *args):
327 """ print as Python print would do
328 """
329 if self.output:
330 for arg in args:
331 print arg,
332 print
333
Note: See TracBrowser for help on using the repository browser.