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

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