source: fact/tools/PyDimCtrl/make_fix_dimservers.py@ 15063

Last change on this file since 15063 was 14884, checked in by neise, 12 years ago
changed the layout of fact_dim_servers.py
  • Property svn:executable set to *
File size: 14.8 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
18#pydim.dic_set_dns_node('daq')
19pydim.dic_set_dns_node('newdaq')
20
21def print_command( name, f ):
22 meth_name = name.split('/')[1].lower()
23 if iskeyword(meth_name):
24 meth_name += '_cmd'
25
26 doc_string = ""
27 doc_string += "Dim Command \n"
28 if name in dd:
29 if not dd[name]:
30 doc_string += "DESC in SERVICE_DESC is empty ?!"
31 else:
32 doc_string += dd[name]
33 else:
34 doc_string += "-- no DESC found in SERVICE_DESC --"
35 doc_string += '\n'
36 doc_string += services[name.split('/')[0]][name][0]
37
38
39 # this is the new command, it simply calls the _cmd() method
40 f.write(" def " + meth_name + "(self, *args):\n" )
41 f.write(' """ \n')
42 for line in doc_string.split('\n'):
43 if '|' in line:
44 for subline in line.split('|'):
45 if subline:
46 f.write(' ' + subline + '\n' )
47 else:
48 f.write(' ' + line + '\n' )
49
50 f.write(' """ \n')
51 f.write(' self._cmd("' + meth_name.upper() + '", *args)\n\n' )
52
53def print_getter( name, f ):
54 meth_name = name.split('/')[1].lower()
55 if iskeyword(meth_name):
56 meth_name += '_get'
57
58 doc_string = ""
59 doc_string += "Dim Service \n"
60 if name in dd:
61 if not dd[name]:
62 doc_string += "DESC in SERVICE_DESC is empty ?!"
63 else:
64 doc_string += dd[name]
65 else:
66 doc_string += "-- no DESC found in SERVICE_DESC --"
67 doc_string += '\n'
68 doc_string += services[name.split('/')[0]][name][0]
69
70
71 # this is the new command, it simply calls the _cmd() method
72 f.write(" def " + meth_name + "(self):\n" )
73 f.write(' """ \n')
74 for line in doc_string.split('\n'):
75 if '|' in line:
76 for subline in line.split('|'):
77 if subline:
78 f.write(' ' + subline + '\n' )
79 else:
80 f.write(' ' + line + '\n' )
81 f.write(' """ \n')
82 f.write(' return self._get("' + meth_name.upper() + '")\n\n' )
83
84
85
86
87class FactDimServer( object ):
88 def __init__(self, name, services):
89 """ sets name of instance to name of server, all uppercase
90 """
91 self.list_of_states = []
92 self.name = name.upper()
93 self.print_state = False
94 self.print_msg = False
95 self.reg_state_cb()
96 self.reg_msg_cb()
97 self.user_func = None
98 self.__delay_between_cmds = 1.0
99 self.__delay_between_services = 1.0
100 self.__last_cmd_send = -1*float('inf')
101 self.__last_service_got = -1*float('inf')
102 self.services = services
103
104 def _cmd(self, cmdstr, *args):
105 """ used by all dynamicly created methods, which call a Dim CMD
106 """
107 cmdstr=self.name+'/'+cmdstr.upper()
108 desc = self.services[self.name][cmdstr.upper()][0]
109
110 # there is a work around for a bug in pydim
111 # even if a command needs no argument, and desc is also empty string
112 # one has to give one ... need to tell Niko about it.
113 if not desc:
114 desc = 'I'
115 args=(1,)
116 elif desc == 'O':
117 args = (0,)
118 while not time.time() - self.__last_cmd_send > self.__delay_between_cmds:
119 time.sleep(0.5)
120 self.__last_cmd_send = time.time()
121 pydim.dic_sync_cmnd_service(cmdstr, args, desc, timeout=1)
122
123
124
125 def _get(self, service):
126 """ used by all dynamicly created methods, which get a service
127 """
128 full_srv_name = self.name+'/'+service.upper()
129 desc = self.services[self.name][full_srv_name][0]
130
131 while not time.time() - self.__last_service_got > self.__delay_between_services:
132 time.sleep(0.5)
133 self.__last_service_got = time.time()
134 #print 'full_srv_name',full_srv_name
135 #print 'desc', desc
136 return pydim.dic_sync_info_service(full_srv_name, desc, timeout=1)
137
138
139
140 def __call__(self):
141 """ Wrapper / For Convenience
142 self.state() returns a string (if it exists)
143 *returns* numeric state code, parsed from return of self.state()
144 """
145 if hasattr(self, 'stn'):
146 return self.stn
147 else:
148 raise TypeError(self.name+' has no CMD called STATE')
149
150 def wait(self, state_num, timeout=None):
151 """ waits for a certain state
152 BLOCKING
153 returns True if state was reached
154 returns False if timeout occured
155 raises TypeError if Server has no method state
156 """
157
158 if not hasattr(self, 'stn'):
159 raise TypeError(self.name+' has no CMD called STATE')
160 if timeout == None:
161 timeout = float('inf')
162 else:
163 timeout = float(timeout)
164 start = time.time()
165 while not self.stn == state_num:
166 time.sleep(0.1)
167 if time.time() >= start+timeout:
168 return False
169 return True
170
171 def state_callback(self, state):
172 self.sts = state
173 try:
174 self.stn = int(state[state.find('[')+1 : state.find(']')])
175 except ValueError:
176 self.stn = None
177
178 self.last_st_change = time.time()
179 self.list_of_states.append( (self.last_st_change, self.stn) )
180 if len(self.list_of_states) > 10000:
181 print "list_of_states too long, truncating..."
182 self.list_of_states = self.list_of_states[1000:]
183
184 if self.user_func:
185 self.user_func( self.stn )
186
187 if self.print_state:
188 print state
189
190 def msg_callback(self, msg):
191 if self.print_msg:
192 print msg
193
194 def reg_state_cb(self):
195 if not hasattr(self, 'state'):
196 raise TypeError(self.name+' has no CMD called STATE')
197 service_name = self.name.upper()+'/STATE'
198 self.state_sid = pydim.dic_info_service(service_name, "C", self.state_callback)
199 if not self.state_sid:
200 del self.state_sid
201 raise IOError('could not register STATE client')
202
203 def reg_msg_cb(self):
204 if not hasattr(self, 'state'):
205 raise TypeError(self.name+' has no CMD called STATE')
206 service_name = self.name.upper()+'/MESSAGE'
207 self.msg_sid = pydim.dic_info_service(service_name, "C", self.msg_callback)
208 if not self.msg_sid:
209 del self.msg_sid
210 raise IOError('could not register MESSAGE client')
211
212 def unreg_state_cb(self):
213 if hasattr(self, 'state_sid'):
214 pydim.dic_release_service(self.state_sid)
215 del self.state_sid
216
217 def unreg_msg_cb(self):
218 if hasattr(self, 'msg_sid'):
219 pydim.dic_release_service(self.msg_sid)
220 del self.msg_sid
221
222 def __del__(self):
223 self.unreg_state_cb()
224 self.unreg_msg_cb()
225
226
227
228# In order to create classes according to the Dim-Servers, currently connected
229# to the DIS_DNS I have to parse DIS_DNS/SERVER_LIST
230# This is done in two steps, first I get the list of Server Names from DIS_DNS
231# and the I get the list of each servers services and cmds,
232# from each servers SERVICE_LIST and the service/command description
233# from each servers SERVICE_DESC
234# I get quite a lot of information, which I store in python dicts, or
235# even nested dicts, if necessary.
236def ParseDnsServerList( timeout = 5):
237 """ requests DIS_DNS/SERVER_LSIT and creates dict *servers* from it,
238 which is returned as well.
239
240 the returned dict contains:
241 { "SERVER_NAME": (pid_of_server_process, host_the_server_runs_on) }
242
243 the SERVER_NAME seems to be in capital letter all the time, but I'm
244 not sure if this is really ensured by Dim or if this was ensured by
245 Thomas manually. So maybe one should not rely on this.
246
247 In case the request of DIS_DNS/SERVER_LIST times out, no Exception
248 is thrown, but the program exits. This is probably bullshit.
249 """
250 # making server list
251 rawlist = pydim.dic_sync_info_service('DIS_DNS/SERVER_LIST','C', timeout=5)
252 if rawlist == None:
253 ## todo: throw an exception here!
254 print "couldn't get the server list of DIS_DNS. program abortion..."
255 sys.exit(1)
256 #print rawlist
257 # the output needs to be treated a bit .. it is a tuple with only one long string in it
258 # the string contains | and the strange character \x00 (ascii-zero)
259 # I use both to cut the list apart
260 rawlist = rawlist[0].split('\x00')
261 servers_n_hosts = rawlist[0].split('|')
262 server_ids = rawlist[1].split('|')
263
264 servers = {}
265 for i,snh in enumerate(servers_n_hosts):
266 snh = snh.split('@')
267 s = snh[0]
268 h = snh[1]
269 sid = server_ids[i]
270 servers[s] = (sid, h)
271
272 return servers
273
274
275
276# servers should be a dict containing uppercase server names as keys,
277# the dict-values are not needed, so it might be any iteratable python listlike type
278# to be precise
279def ParseServersServiceList( servers ):
280 """ this function parses SERVER_NAME/SERVICE_LIST as well as
281 SERVER_NAME/SERVICE_DESC of all SERVER_NAME in the input list-like *servers*
282
283 two dicts are returned:
284 *services* - nested dict:
285 { "SERVER_NAME" : { "SERVER_NAME/SERVICE_NAME" : (
286 "format-description-string like: I:2;C",
287 "" or "CMP" or "RPC",
288 "service description text" ) } }
289
290 *dd* - normal dict:
291 { "SERVER_NAME/SERVICE_NAME" : "service description text" }
292
293 """
294
295 services = {}
296 dd = {}
297 for server in servers:
298 # sl_raw is a tuple, with one really long string, which needs to be parsed
299 sl_raw = pydim.dic_sync_info_service(server+'/SERVICE_LIST','C',timeout=3)[0]
300 if sl_raw == None:
301 print "couldn't get the service list of ", server, "program abortion..."
302 sys.exit(1)
303# print server
304# print sl_raw
305 # even without parsing, I can find out, if this server also gives me a
306 # service description list. In case it does not, this is fine as well
307 # the doc string of the dynamicly created methods, will then contain
308 # a note, that therer was no SERVICE_DESC ...
309 if server+'/SERVICE_DESC' in sl_raw:
310 sd_raw = pydim.dic_sync_info_service(server+'/SERVICE_DESC','C',timeout=3)[0]
311 if sd_raw == None:
312 print "couldn't get the service description list of ", server, "program abortion..."
313 sys.exit(1)
314 else:
315 sd_raw = ''
316
317 # now before parsing, I strip off all ASCII zeroes '\x00' and all
318 # line breaks off the *end* of the long string of both
319 # the service list sl
320 # and service description sd
321 #
322 # I think in sl_raw were alse some '\x00' in the middle .. these
323 # are replaced by nothing, in case they are there.
324 sl_raw = sl_raw.rstrip('\x00\n')
325 sl_raw = sl_raw.replace('\x00','')
326 sd_raw = sd_raw.rstrip('\x00\n')
327
328 # The lists are seperated by line breaks, so I split them using this
329 sl = sl_raw.split('\n')
330 sd = sd_raw.split('\n')
331
332 # First I parse the service descriptons, so I have them available,
333 # when I create the dict full of services.
334 # All desciptions look like this
335 # 'SERVER/SERVICE=some descriptive text' or
336 # 'SERVER/SERVICE='
337 # this I use to create the dictionary.
338 # create descripton dict dd from service_desc list sd
339 for d_str in sd:
340 service,equalsign,desc = d_str.partition('=')
341 if not '/' in service:
342 service = server+'/'+service
343 #if '=' != equalsign:
344 # print "Error: server:", server, "desc:", d_str
345 dd[service] = desc
346
347 # Now I fill ther services dict. Each server gets a nested dict
348 # inside services.
349 # Each service is explained in a string with a '|' in between.
350 # I use this for spliting.
351 # The string look like this
352 # SERVER/SERVICE|format-desc-str(e.g. I:2;C)|-empty- or CMD or RPC|
353 services[server] = {}
354 for service in sl:
355 service = service.split('|')
356 if service[0] in dd:
357 services[server][service[0]] = (
358 service[1], service[2], dd[service[0]])
359 return services, dd
360
361
362
363if __name__ == '__main__':
364
365 print 'requesting and parsing DIM DNS server list'
366 servers = ParseDnsServerList()
367 print '... parsing each servers service list'
368 services, dd = ParseServersServiceList( servers )
369
370 # since I want this text to created for reference
371 # I will create it here
372 print "creating fix class definitions in a file:"
373 f = open("fact_dim_servers.py", "w")
374 f.write("from make_fix_dimservers import FactDimServer\n")
375
376 for server_name in servers:
377
378 if server_name == "DIS_DNS":
379 continue
380
381 f.write("class "+server_name+" ( FactDimServer): \n")
382
383 for cmd in services[server_name]:
384 cmdname = cmd.split('/')
385 if len(cmdname) > 1:
386 cmdname = cmdname[1]
387 elif len(cmdname) >0:
388 cmdname = cmdname[0]
389 cmd = server_name+'/'+cmdname
390 else:
391 print server_name, cmd
392 raise ValueError('was not able to parse service/command names')
393 if 'CMD' in services[server_name][cmd][1]:
394 print_command(cmd, f)
395 elif not services[server_name][cmd][1]:
396 print_getter(cmd, f)
397
398
399 f.close()
400
401 import fact_dim_servers
402
403 # create an instace of each of the classes
404 # and make it globally known, i.e. known to the Python interpreter
405 # all the ServerClass instances are collected in a list
406 # so one can get a quick overview --> print dims
407 print 'creating an instance of each FACT DIM server'
408
409 dims = []
410 new_instance = None
411 for server_name in servers:
412 if server_name == 'DIS_DNS':
413 continue
414 new_instance = getattr(sys.modules["fact_dim_servers"], server_name)(server_name, services)
415 dims.append( new_instance )
416 globals()[server_name.lower()] = new_instance
417 del new_instance
418
419 print '.... fact DIM servers are ready to use'
420 print '-'*70
421 print
422
Note: See TracBrowser for help on using the repository browser.