1 | # -*- coding: utf-8 -*- |
---|
2 | # |
---|
3 | # Copyright (C) 2008-2010 Marcus Walther (walther@szgrf.bgr.de) |
---|
4 | # |
---|
5 | # This file is part of Seismic Handler eXtended (SHX) |
---|
6 | # Full details can be found at project website http://www.seismic-handler.org/ |
---|
7 | # |
---|
8 | # SHX is free software; you can redistribute it and/or modify |
---|
9 | # it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by |
---|
10 | # the Free Software Foundation; either version 3 of the License, or |
---|
11 | # (at your option) any later version. |
---|
12 | # |
---|
13 | # SHX is distributed in the hope that it will be useful, |
---|
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
16 | # GNU Lesser General Public License for more details. |
---|
17 | # |
---|
18 | # You should have received a copy of the GNU Lesser General Public License |
---|
19 | # along with SHX (see license.txt); if not, write to the Free Software |
---|
20 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
---|
21 | |
---|
22 | """wrapper for seismic handler c library |
---|
23 | |
---|
24 | - implemented as multiton (which does not work at the moment, because c |
---|
25 | library is accessed as shared library, which can only loaded once into |
---|
26 | one process |
---|
27 | - maybe this will be changed later using parallel processing methods""" |
---|
28 | |
---|
29 | import ctypes as C |
---|
30 | import sys |
---|
31 | import os |
---|
32 | from copy import copy |
---|
33 | from StringIO import StringIO |
---|
34 | |
---|
35 | from SeismicHandler.core.log import logging |
---|
36 | from SeismicHandler.core.error import MultipleLibError, CommandStreamError, GotoTargetNotFoundError, CommandNotSuccessfulError |
---|
37 | from SeismicHandler.config.options import environment |
---|
38 | from SeismicHandler.core.shheaders import LINELENGTH, NOERROR, PARAM, CAPCNV, EMPTYVALUES |
---|
39 | from SeismicHandler.core.modules.Messages import MessageService, Message |
---|
40 | |
---|
41 | logger = logging.getLogger("core.shlib") |
---|
42 | |
---|
43 | class shlib(object): |
---|
44 | _instances = {} |
---|
45 | _active = "" |
---|
46 | |
---|
47 | def __new__(cls, id="_standard", *args, **kwargs): |
---|
48 | """ensure single instance with identifier "xyz" |
---|
49 | |
---|
50 | - requested instance will be set as active""" |
---|
51 | |
---|
52 | # issue regarding static vars not solved, so raise exception |
---|
53 | if id != "_standard": |
---|
54 | raise MultipleLibError |
---|
55 | |
---|
56 | if not id in cls._instances: |
---|
57 | cls._instances[id] = object.__new__(cls) |
---|
58 | |
---|
59 | cls._active = id |
---|
60 | |
---|
61 | return cls._instances[id] |
---|
62 | |
---|
63 | def __init__(self, *args, **kwargs): |
---|
64 | if not "sh" in self.__dict__: |
---|
65 | # load shared lib (once per identifier) |
---|
66 | |
---|
67 | try: |
---|
68 | self.sh = C.CDLL(os.path.join(environment["root"], "libshc.so")) |
---|
69 | logger.debug(self.sh) |
---|
70 | |
---|
71 | except Exception, e: |
---|
72 | logger.critical(("error loading shared lib", e)) |
---|
73 | sys.exit(1) |
---|
74 | |
---|
75 | self._init() |
---|
76 | |
---|
77 | def _init(self): |
---|
78 | """start up setup of shlib""" |
---|
79 | |
---|
80 | slib = self.sh |
---|
81 | |
---|
82 | # somewhat needless, since c lib doesn't deal which command line parameters |
---|
83 | status= C.c_int() |
---|
84 | CSTR_ARRAY = C.c_char_p * 1 |
---|
85 | argv = CSTR_ARRAY("") |
---|
86 | |
---|
87 | slib.GpReadParfile() |
---|
88 | slib.se_initialize(len(argv), argv, C.byref(status)) |
---|
89 | |
---|
90 | if status.value == NOERROR: |
---|
91 | logger.debug("init of shared lib id %s succeeded" % self._active) |
---|
92 | else: |
---|
93 | logger.critical("init of shared lib id %s failed" % self._active) |
---|
94 | sys.exit(2) |
---|
95 | |
---|
96 | self._startup() |
---|
97 | |
---|
98 | idxu = Message(MessageService.INFOIDXUPDATE) |
---|
99 | MessageService.subscribe(idxu, self._readIdxStructure) |
---|
100 | MessageService.trigger(idxu) |
---|
101 | |
---|
102 | def _readIdxStructure(self, *args, **kwargs): |
---|
103 | idxlist = { |
---|
104 | "long": C.c_long, |
---|
105 | "int": C.c_int, |
---|
106 | "byte": C.c_byte, |
---|
107 | "real": C.c_float, |
---|
108 | "string": C.c_char_p, |
---|
109 | "char": C.c_char, |
---|
110 | "time": lambda x: x, |
---|
111 | "pointer": C.pointer, |
---|
112 | "flag": lambda x: x, |
---|
113 | "xtra": lambda x: x |
---|
114 | } |
---|
115 | |
---|
116 | slib = self._instances[self._active].sh |
---|
117 | |
---|
118 | idxstructure = {} |
---|
119 | |
---|
120 | for idx in idxlist: |
---|
121 | idx_1 = idx[0] |
---|
122 | |
---|
123 | i = C.c_int.in_dll(slib, "icnt_%c_dbv" % idx_1) |
---|
124 | |
---|
125 | nam = ((C.c_char*11)*i.value).in_dll(slib, "inam_%c_dbv" % idx_1) |
---|
126 | ent = (C.c_uint*i.value).in_dll(slib, "ient_%c_dbv" % idx_1) |
---|
127 | |
---|
128 | for e in range(i.value): |
---|
129 | # structure maps name to type, ientry value, index and function |
---|
130 | idxstructure[nam[e].value.replace("-", "_")] = (idx, ent[e], ent[e] & 0x03ff, idxlist[idx]) |
---|
131 | |
---|
132 | self.idx = idxstructure |
---|
133 | |
---|
134 | def setInfoEntry(self, trc, name, value): |
---|
135 | """transfer info entry "name" to shlib instance for trace at address trc |
---|
136 | |
---|
137 | """ |
---|
138 | |
---|
139 | map = { |
---|
140 | "long": "db_setl", |
---|
141 | "int": "db_seti", |
---|
142 | "byte": "db_setb", |
---|
143 | "string": "db_sets", |
---|
144 | "char": "db_setc", |
---|
145 | "real": "db_setr", |
---|
146 | "flag": "db_setf", # XXX |
---|
147 | "pointer": "db_setp", # XXX not needed (still unchangeable) |
---|
148 | "time": "db_sett", |
---|
149 | } |
---|
150 | |
---|
151 | dattype, ientry, _, vmap = self.idx[name.upper()] |
---|
152 | |
---|
153 | fct = getattr(self.sh, map[dattype]) |
---|
154 | |
---|
155 | status = C.c_int() |
---|
156 | |
---|
157 | if value == None: |
---|
158 | value = EMPTYVALUES[dattype] |
---|
159 | else: |
---|
160 | value = vmap(value) |
---|
161 | |
---|
162 | if dattype == "time": |
---|
163 | fct(trc, ientry, C.byref(value), C.byref(status)) |
---|
164 | else: |
---|
165 | fct(trc, ientry, value, C.byref(status)) |
---|
166 | |
---|
167 | MessageService.trigger(Message(MessageService.TRACEUPDATE)) |
---|
168 | if status.value: |
---|
169 | logger.error("could not set info entry '%s'" % name) |
---|
170 | else: |
---|
171 | logger.debug("set info entry '%s' to '%s' for trace %u" % (name, str(value), trc)) |
---|
172 | |
---|
173 | def _startup(self): |
---|
174 | """set some start up parameters""" |
---|
175 | |
---|
176 | # self.call(StringIO("SHSTRTUP")) |
---|
177 | # return |
---|
178 | |
---|
179 | self.call(StringIO("wdw/dc=s/ic=s/main")) |
---|
180 | self.call(StringIO("wdw/ic=s")) |
---|
181 | |
---|
182 | # new info entries |
---|
183 | self.call(StringIO("entry define sign i 6 11")) |
---|
184 | self.call(StringIO("entry define eventno i 7 12")) |
---|
185 | self.call(StringIO("entry define mark i 8 14")) |
---|
186 | self.call(StringIO("entry define calib r 1 26")) |
---|
187 | self.call(StringIO("entry define distance r 2 11")) |
---|
188 | self.call(StringIO("entry define azimuth r 4 12")) |
---|
189 | self.call(StringIO("entry define slowness r 3 18")) |
---|
190 | self.call(StringIO("entry define inci r 12 13")) |
---|
191 | self.call(StringIO("entry define depth r 13 14")) |
---|
192 | self.call(StringIO("entry define magnitude r 14 15")) |
---|
193 | self.call(StringIO("entry define lat r 15 16")) |
---|
194 | self.call(StringIO("entry define lon r 16 17")) |
---|
195 | self.call(StringIO("entry define signoise r 17 22")) |
---|
196 | self.call(StringIO("entry define pwdw r 18 23")) |
---|
197 | self.call(StringIO("entry define dcvreg r 19 24")) |
---|
198 | self.call(StringIO("entry define dcvinci r 20 25")) |
---|
199 | self.call(StringIO("entry define filter s 10 11")) |
---|
200 | self.call(StringIO("entry define quality s 11 12")) |
---|
201 | self.call(StringIO("entry define p-onset t 1 22")) |
---|
202 | self.call(StringIO("entry define s-onset t 2 23")) |
---|
203 | self.call(StringIO("entry define origin t 3 24")) |
---|
204 | |
---|
205 | def call(self, stream): |
---|
206 | """call original c lib function to execute SH command in cmd |
---|
207 | |
---|
208 | GOTO command is replaced by own implementation |
---|
209 | |
---|
210 | @param stream: stream containing SH command(s) (one per line) |
---|
211 | """ |
---|
212 | |
---|
213 | if not hasattr(stream, "readline"): |
---|
214 | raise CommandStreamError |
---|
215 | |
---|
216 | slib = self._instances[self._active].sh |
---|
217 | cmd = PARAM() |
---|
218 | status = C.c_int() |
---|
219 | |
---|
220 | # analyse stream |
---|
221 | script = Scripting(stream) |
---|
222 | |
---|
223 | # now run |
---|
224 | while True: |
---|
225 | # read next command |
---|
226 | try: |
---|
227 | cmdstr = script.next() |
---|
228 | except StopIteration: |
---|
229 | break |
---|
230 | |
---|
231 | # make changeable string (strings in python may no be altered!) |
---|
232 | cmdstr = C.create_string_buffer(cmdstr) |
---|
233 | |
---|
234 | # upper case convert? |
---|
235 | shflags = C.c_int.in_dll(slib, "shflags_shv") |
---|
236 | if shflags.value & CAPCNV: |
---|
237 | logger.debug("changing case of command") |
---|
238 | slib.ut_cap(cmdstr) |
---|
239 | |
---|
240 | # parse command |
---|
241 | slib.cp_parse(cmdstr, C.byref(cmd), C.byref(status)) |
---|
242 | |
---|
243 | # substitute variables |
---|
244 | if status.value == NOERROR: |
---|
245 | slib.tr_partrans(C.byref(cmd), C.byref(status)) |
---|
246 | else: |
---|
247 | raise CommandNotSuccessfulError(cmdstr.value, status.value) |
---|
248 | |
---|
249 | logger.debug("command elements: %s", [y.value for y in cmd.p[:cmd.pno+1]]) |
---|
250 | logger.debug("qualifier elements: %s", [y.value for y in cmd.q[:cmd.qno+1]]) |
---|
251 | |
---|
252 | # GOTO |
---|
253 | if cmd.p[0].value.upper() == "GOTO": |
---|
254 | script.Goto(cmd.p[1].value) |
---|
255 | continue |
---|
256 | |
---|
257 | # IF -> GOTO |
---|
258 | if cmd.p[0].value.upper() == "IF" and cmd.p[4].value.upper() == "GOTO": |
---|
259 | script.IfGoto(cmd) |
---|
260 | continue |
---|
261 | |
---|
262 | # all other commands are passed to slib |
---|
263 | slib.err_clearcontext() |
---|
264 | |
---|
265 | execstr = C.create_string_buffer(LINELENGTH) |
---|
266 | quit = C.c_int() |
---|
267 | redraw = C.c_int() |
---|
268 | iscmdproc = C.c_int() |
---|
269 | rdlevel = C.c_int() |
---|
270 | |
---|
271 | # check for prompt (not set if running non-interactively |
---|
272 | try: |
---|
273 | ptstr = sys.ps1 |
---|
274 | except AttributeError: |
---|
275 | ptstr = "" |
---|
276 | |
---|
277 | prompt = C.create_string_buffer(ptstr, LINELENGTH) |
---|
278 | |
---|
279 | # save tc, cc channel |
---|
280 | tc = C.c_int.in_dll(slib, "tc") |
---|
281 | cc = C.c_int.in_dll(slib, "cc") |
---|
282 | gc = C.c_int.in_dll(slib, "gc") |
---|
283 | |
---|
284 | slib.ui_level.restype = C.c_int |
---|
285 | slib.ui_levelname.restype = C.c_char_p |
---|
286 | |
---|
287 | if status.value == NOERROR: |
---|
288 | logger.debug("before: tc=%u, cc=%u, gc=%s, ui=%u" % (tc.value, cc.value, gc.value, slib.ui_level())) |
---|
289 | slib.ui_setconsole(cc) |
---|
290 | |
---|
291 | slib.se_execute_command( |
---|
292 | C.byref(cmd), cmdstr, execstr, C.byref(quit), C.byref(redraw), |
---|
293 | C.byref(iscmdproc), C.byref(rdlevel), prompt, C.byref(status)) |
---|
294 | |
---|
295 | if redraw.value: |
---|
296 | MessageService.trigger(Message(MessageService.REDRAW)) |
---|
297 | |
---|
298 | # print quit.value, redraw.value, iscmdproc.value, rdlevel.value, status.value |
---|
299 | |
---|
300 | logger.debug("after: tc=%u, cc=%u, gc=%s, ui=%u" % (tc.value, cc.value, gc.value, slib.ui_level())) |
---|
301 | |
---|
302 | if status.value == NOERROR and iscmdproc.value: |
---|
303 | cmdproc = slib.ui_levelname(slib.ui_level()) |
---|
304 | logger.info("command procedure found: %s" % cmdproc) |
---|
305 | self.call(open(cmdproc)) |
---|
306 | |
---|
307 | if status.value != NOERROR: |
---|
308 | logger.info("execution not successful, error code %u" % status.value) |
---|
309 | slib.se_dsplymsg(cc, status) |
---|
310 | raise CommandNotSuccessfulError(cmdstr.value, status.value) |
---|
311 | else: |
---|
312 | pass |
---|
313 | |
---|
314 | return |
---|
315 | |
---|
316 | def switch(self, id): |
---|
317 | """switch between parallel instances of shlib |
---|
318 | |
---|
319 | @param id: switch to shlib instance by identifier |
---|
320 | If requested instance is not found, it will be created on the fly!""" |
---|
321 | |
---|
322 | self._oldactive = self._active |
---|
323 | |
---|
324 | if id in self._instances: |
---|
325 | logger.debug("shlib instance switched to %s" % id) |
---|
326 | self._active = id |
---|
327 | else: |
---|
328 | # start new instance |
---|
329 | logger.info("id %s not found, start new one!" % id) |
---|
330 | shlib(id) |
---|
331 | |
---|
332 | def switchBack(self): |
---|
333 | """switch back to former instance |
---|
334 | |
---|
335 | If attribute doesn't exist just log message, no Exception raised. |
---|
336 | |
---|
337 | Note: If former instance was quit before, a new one with same name |
---|
338 | will be created on the fly!""" |
---|
339 | |
---|
340 | try: |
---|
341 | self.switch(self._oldactive) |
---|
342 | except AttributeError: |
---|
343 | logger.warning("Could not switch back to former instance") |
---|
344 | |
---|
345 | def quit(self): |
---|
346 | """shut down active shlib instance""" |
---|
347 | |
---|
348 | logger.debug("shut down id %s" % self._active) |
---|
349 | |
---|
350 | # last session terminates everything |
---|
351 | # static variables are shared, so not double freeing occurs |
---|
352 | if len(self._instances) == 1: |
---|
353 | self._instances[self._active].sh.se_terminate() |
---|
354 | self._instances[self._active].sh.SqlDeleteScratchFile() |
---|
355 | |
---|
356 | del self._instances[self._active] |
---|
357 | |
---|
358 | try: |
---|
359 | self._active = self._instances.keys()[-1] |
---|
360 | except IndexError: |
---|
361 | self._active = "" |
---|
362 | |
---|
363 | def quitAll(self): |
---|
364 | """quit all shlib instances""" |
---|
365 | |
---|
366 | for id in copy(self._instances): |
---|
367 | self.switch(id) |
---|
368 | self.quit() |
---|
369 | |
---|
370 | class Scripting(object): |
---|
371 | def __init__(self, stream): |
---|
372 | content = [] |
---|
373 | targets = {} |
---|
374 | skipped = 0 |
---|
375 | |
---|
376 | for line, cmdstr in enumerate(stream): |
---|
377 | cmdstr = cmdstr.strip() |
---|
378 | |
---|
379 | # skip empty lines or comments |
---|
380 | if len(cmdstr) and cmdstr[0] != "!": |
---|
381 | # remember goto target line? |
---|
382 | if cmdstr.split()[0][-1] == ":": |
---|
383 | targets[cmdstr.upper()] = line - skipped |
---|
384 | |
---|
385 | content.append(cmdstr) |
---|
386 | else: |
---|
387 | skipped += 1 |
---|
388 | |
---|
389 | self.content = content |
---|
390 | self.targets = targets |
---|
391 | self.pointer = 0 |
---|
392 | |
---|
393 | def next(self): |
---|
394 | try: |
---|
395 | # skip goto targets |
---|
396 | while self.pointer in self.targets.values(): |
---|
397 | self.pointer += 1 |
---|
398 | |
---|
399 | pnt = self.pointer |
---|
400 | self.pointer += 1 |
---|
401 | |
---|
402 | return self.content[pnt] |
---|
403 | except IndexError: |
---|
404 | raise StopIteration |
---|
405 | |
---|
406 | def Goto(self, target): |
---|
407 | try: |
---|
408 | self.pointer = self.targets[target.upper()] |
---|
409 | except KeyError: |
---|
410 | raise GotoTargetNotFoundError(target.upper()) |
---|
411 | |
---|
412 | def IfGoto(self, cmd): |
---|
413 | # cast format |
---|
414 | cast = { |
---|
415 | "S": str, |
---|
416 | "I": int, |
---|
417 | "R": float |
---|
418 | } |
---|
419 | |
---|
420 | cmp, check = cmd.p[2].value, cmd.p[2].value[:2] |
---|
421 | |
---|
422 | var1 = cast[cmp[-1]](cmd.p[1].value) |
---|
423 | var2 = cast[cmp[-1]](cmd.p[3].value) |
---|
424 | |
---|
425 | # comparison |
---|
426 | try: |
---|
427 | comp = { |
---|
428 | "EQ": var1.__eq__, |
---|
429 | "NE": var1.__ne__, |
---|
430 | "GT": var1.__gt__, |
---|
431 | "GE": var1.__ge__, |
---|
432 | "LT": var1.__lt__, |
---|
433 | "LE": var1.__le__, |
---|
434 | } |
---|
435 | except AttributeError: |
---|
436 | # in python 2.5 integers have no __eq__, __gt__, ... methods :( |
---|
437 | comp = { |
---|
438 | "EQ": lambda x: var1 == x, |
---|
439 | "NE": lambda x: var1 != x, |
---|
440 | "GT": lambda x: var1 > x, |
---|
441 | "GE": lambda x: var1 >= x, |
---|
442 | "LT": lambda x: var1 < x, |
---|
443 | "LE": lambda x: var1 <= x, |
---|
444 | } |
---|
445 | |
---|
446 | # check condition |
---|
447 | if comp[check](var2): |
---|
448 | self.Goto(cmd.p[5].value) |
---|
449 | |
---|
450 | if __name__ == "__main__": |
---|
451 | s = shlib() |
---|