00001 #!/usr/local/bin/python 00002 # 00003 # Copyright 2002 00004 # by 00005 # The Board of Trustees of the 00006 # Leland Stanford Junior University. 00007 # All rights reserved. 00008 # 00009 00010 __facility__ = "Online" 00011 __abstract__ = "GLAST LAT run control suite execution framework" 00012 __author__ = "S. Tuvi <stuvi@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online" 00013 __date__ = ("$Date: 2005/10/26 01:46:50 $").split(' ')[1] 00014 __version__ = "$Revision: 2.22 $" 00015 __credits__ = "SLAC" 00016 00017 import LATTE.copyright_SLAC 00018 00019 import threading 00020 import logging as log 00021 import types 00022 import copy 00023 00024 import rcUtil 00025 from RunControlCommon import RunControlCommon 00026 00027 class rcSuite(object): 00028 00029 class StopRequestedException(Exception): 00030 def __init__(self,msg,suite,module): 00031 self.args = (msg, suite, module) 00032 self.msg = msg 00033 self.suite = suite 00034 self.module = module 00035 00036 class SuiteCallbacks(object): 00037 def beforeSetupSuite(self, suite): 00038 pass 00039 def afterSetupSuite(self, suite): 00040 pass 00041 def beforeStartSuite(self, suite): 00042 pass 00043 def afterStartSuite(self, suite): 00044 pass 00045 def beforeStopSuite(self, suite): 00046 pass 00047 def afterStopSuite(self, suite): 00048 pass 00049 00050 class StandaloneLauncher(object): 00051 def __init__(self, logLevel="INFO"): 00052 self.__common = RunControlCommon() 00053 self.__common.initialize() 00054 self.__common.setLoggingLevel(logLevel) 00055 self.__common.connect() 00056 00057 def launch(self, suiteClass, user, callbacks=None): 00058 if suiteClass is None: 00059 module = self.__common.options().app 00060 try: 00061 (module, filename) = rcUtil.importModule(module, reload=0) 00062 except Exception, e: 00063 log.exception(e) 00064 return 00065 us = module.userSuite(self, user, self.__common) 00066 if callbacks is None and 'userSuiteCallbacks' in dir(module): 00067 callbacks = module.userSuiteCallbacks() 00068 else: 00069 us = suiteClass(self, user, self.__common) 00070 if callbacks is not None: 00071 callbacks.beforeSetupSuite(us) 00072 us.rcSetupSuite() 00073 if callbacks is not None: 00074 callbacks.afterSetupSuite(us) 00075 if callbacks is not None: 00076 callbacks.beforeStartSuite(us) 00077 us.rcStartSuite(spawn=0) 00078 if callbacks is not None: 00079 callbacks.afterStartSuite(us) 00080 if callbacks is not None: 00081 callbacks.beforeStopSuite(us) 00082 us.rcStopSuite() 00083 if callbacks is not None: 00084 callbacks.afterStopSuite(us) 00085 00086 def doStopRun(self): 00087 pass 00088 00089 def preferences(self): 00090 return self.__common.prefMan().preferences() 00091 00092 def common(self): 00093 return self.__common 00094 00095 00096 def __init__(self, gui, userId, debug): 00097 log.debug("rcSuite.__init__()") 00098 00099 self.__userId = userId 00100 self.__debug = debug 00101 self.__cmdCli = None 00102 self.__evtCli = None 00103 00104 #self.__modules = [] 00105 #self.__schemas = [] 00106 self.__isOKtoQuitOrPause = threading.Event() 00107 self.__cmdQuit = 0 00108 self.__paused = False 00109 self.__running = False 00110 self.__app = None 00111 self.__scriptSeq = 0 00112 self.__suite = None 00113 self.__suiteInst = None 00114 self.__state = 'RESET' 00115 self.__appState = 'RESET' 00116 # Flag to indicate that there was an exception in the running script. 00117 self.__reset = False 00118 00119 # The following data members are meant to be marked "protected" 00120 self.gui = gui 00121 00122 if self.__debug.__class__.__name__ == 'RunControlCommon': 00123 self.__common = self.__debug 00124 self.__statMon = None 00125 else: 00126 self.__common = self.gui.common() 00127 self.__statMon = self.gui.statusMonitor() 00128 00129 # Expose the schema to the suite 00130 self.lat = self.getSessionVar('_LATSchema') 00131 self.xbrd = self.getSessionVar('_XBRDSchema') 00132 00133 # def addApplication(self, appName, schema): 00134 # try: 00135 # (module, filename) = rcUtil.importModule(appName, reload=0) 00136 # except Exception: 00137 # gutil.logException() 00138 00139 00140 # if 'userApplication' not in dir(module): 00141 # msg = 'Application %s is missing a userApplication class' % appName 00142 # log.error(msg) 00143 # return msg 00144 00145 # log.info("Application loaded from %s" % filename) 00146 # self.__modules.append(module) 00147 # self.__schemas.append(schema) 00148 00149 def setupSuite(self): 00150 """ Override this method to do custom processing before the suite is started and/or 00151 to return a custom schema file 00152 """ 00153 return None 00154 00155 def rcPauseSuite(self): 00156 fn = "rcSuite.rcPauseSuite" 00157 log.debug("%s -- %s" % (fn, self.getName())) 00158 if self.__paused: 00159 return -1 00160 if self.pauseSuite() is not None: 00161 return 00162 if self.__app is not None: 00163 if self.__app.__class__.__name__ == 'userApplication': 00164 self.__isOKtoQuitOrPause.wait() 00165 if self.__app is not None and self.__app.rcPause() is None: 00166 self.__paused = True 00167 return None 00168 else: 00169 if self.__app.rcPauseSuite() is None: 00170 self.__paused = True 00171 return None 00172 return -1 00173 00174 def pauseSuite(self): 00175 """ Override this method to do custom processing before the currently running 00176 application is paused. Pause can be rejected by not returning None 00177 """ 00178 return None 00179 00180 00181 def rcSetupSuite(self): 00182 """ This method can be called from either RunControlMain or from another suite. 00183 """ 00184 fn = "rcSuite.rcSetupSuite" 00185 log.debug("%s -- %s" % (fn, self.getName())) 00186 00187 #askForSchema = 0 00188 #if schema is None: 00189 # reuseSchema = self.getSessionVar('_reuseSchema') 00190 # schema = self.getSessionVar("_suiteLATSchemaFile") 00191 # if not reuseSchema: 00192 # askForSchema = 1 00193 00194 #if askForSchema: 00195 # scmCfg = self.gui.createGUI(rcScmCfgSelector.rcScmCfgSelector, self.gui.common().preferences()['reposdir'], 00196 # schema, self.gui, 'ScmCfgSelector') 00197 # scmCfgFile = scmCfg.getValue() 00198 # # Allow the C++ object to be deleted 00199 # scmCfg.deleteLater() 00200 # if scmCfgFile is None or os.path.splitext(scmCfgFile.lower())[1] != '.xml': 00201 # if scmCfgFile is not None: 00202 # log.error("%s: Invalid SchemaConfig filename '%s'" % (fn, scmCfgFile)) 00203 # schema = None 00204 # else: 00205 # self.setSessionVar("_LATSchemaFile", scmCfgFile) 00206 # self.setSessionVar("_suiteLATSchemaFile", scmCfgFile) 00207 # schema = scmCfgFile 00208 00209 #if schema is not None: 00210 # self.setupSuite() 00211 #else: 00212 00213 #return schema 00214 00215 self.setupSuite() 00216 return 0 00217 00218 def rcStartSuite(self, spawn=1): 00219 fn = "rcSuite.rcStartSuite" 00220 log.debug("%s -- %s" % (fn, self.getName())) 00221 if self.__running and not self.__paused: 00222 return -1 00223 self.__scriptSeq = 0 00224 self.__running = True 00225 self.__state = 'RUNNING' 00226 if self.__paused: 00227 if self.__app.__class__.__name__ == 'userApplication': 00228 if self.__app.rcResume() is None: 00229 self.__paused = False 00230 else: 00231 self.__app.rcStartSuite() 00232 else: 00233 self.__cmdQuit = 0 00234 if spawn: 00235 self.__suiteRun = threading.Thread(group=None, name="SuiteThread", target=self.startSuite) 00236 self.__suiteRun.start() 00237 else: 00238 self.startSuite() 00239 00240 def stopSuite(self): 00241 """ Override this method to do custom processing before the suite is terminated 00242 """ 00243 pass 00244 00245 def rcStopSuite(self): 00246 fn = "rcSuite.rcStopSuite" 00247 log.debug("%s -- %s" % (fn, self.getName())) 00248 if self.stopRequested(): 00249 self.__running = False 00250 return 00251 if self.__app is not None and self.__app.__class__.__name__ == 'userApplication': 00252 self.__isOKtoQuitOrPause.wait() 00253 self.__cmdQuit = 1 00254 if self.__app is not None: 00255 if self.__app.__class__.__name__ == 'userSuite': 00256 self.__app.rcStopSuite() 00257 else: 00258 if self.__paused: 00259 if self.__app.rcResume() is None: 00260 self.__paused = False 00261 self.__app.rcStopRun() 00262 self.__app.rcTeardown() 00263 else: 00264 self.__app.rcStopRun() 00265 self.__app.rcTeardown() 00266 self.stopSuite() 00267 self.__running = False 00268 self.__state = 'RESET' 00269 #self.__suiteRun.join() 00270 00271 #def __suiteRunThread(self): 00272 # i = 0 00273 # for module in self.__modules: 00274 # self.scriptRun(module, self.__schemas[i]) 00275 # if self.__cmdQuit: break 00276 # i += 1 00277 00278 def scriptRun(self, module, context={}): 00279 fn = "rcSuite.scriptRun" 00280 log.debug("%s: %s" % (fn, module)) 00281 self.__scriptSeq += 1 00282 if self.stopRequested(): 00283 raise rcSuite.StopRequestedException("Operator requested stop of execution", 00284 self.getName(), 00285 "" 00286 ) 00287 self.__isOKtoQuitOrPause.clear() 00288 for (key, value) in context.items(): 00289 self.setSessionVar(key, value) 00290 if type(module) is types.StringType: 00291 if self.__statMon is not None: 00292 self.gui.execGUImethod(self.__statMon.stopWatch) 00293 try: 00294 (module, filename) = rcUtil.importModule(module, reload=0) 00295 except Exception, e: 00296 log.exception(e) 00297 if self.__statMon is not None: 00298 self.gui.execGUImethod(self.__statMon.startWatch) 00299 return None 00300 if self.__statMon is not None: 00301 self.gui.execGUImethod(self.__statMon.startWatch) 00302 self.__reset = False 00303 if 'userApplication' in dir(module): 00304 if self.__debug.__class__.__name__ == 'RunControlCommon': 00305 self.__app = module.userApplication(None, self.__userId, 00306 self.__debug ) 00307 else: 00308 self.__app = module.userApplication(self.gui, self.__userId, 00309 self.__debug ) 00310 self.__app.setSuite(self.getName()) 00311 self.__app.setSuiteInstance(self) 00312 self.__app.setCmdCli(self.__cmdCli) 00313 if self.__app.getEvtCli() is None: 00314 self.__app.setEvtCli(self.__evtCli) 00315 self.__appState = 'RESET' 00316 if self.__app.rcSetup() is not None: 00317 self.__cmdQuit = 1 00318 raise rcSuite.StopRequestedException("setup() is rejected, stopping suite execution", 00319 self.__app.getSuite(), 00320 self.__app.getName() 00321 ) 00322 self.__appState = 'STOPPED' 00323 status = self.__app.rcStartRun() 00324 self.__appState = 'RUNNING' 00325 self.__isOKtoQuitOrPause.set() 00326 if status is not None: 00327 self.__cmdQuit = 1 00328 raise rcSuite.StopRequestedException("startRun() is rejected, stopping suite execution", 00329 self.__app.getSuite(), 00330 self.__app.getName() 00331 ) 00332 self.__app.wait() 00333 if self.stopRequested(): 00334 raise rcSuite.StopRequestedException("Operator requested stop of execution", 00335 self.__app.getSuite(), 00336 self.__app.getName() 00337 ) 00338 self.__isOKtoQuitOrPause.wait() 00339 self.__isOKtoQuitOrPause.clear() 00340 if not self.__reset: 00341 self.__app.rcStopRun() 00342 self.__app.rcTeardown() 00343 self.__appState = 'RESET' 00344 del self.__app 00345 self.__app = None 00346 self.__isOKtoQuitOrPause.set() 00347 elif 'userSuite' in dir(module): 00348 self.__app = module.userSuite( self.gui, self.__userId, self.__debug) 00349 log.info("Suite %s loaded from %s" % (self.__app.getName(), filename)) 00350 self.__app.setCmdCli(self.__cmdCli) 00351 self.__app.setEvtCli(self.__evtCli) 00352 #if self.__app.rcSetupSuite(self.getSessionVar("_suiteLATSchemaFile")) is None: 00353 # log.error("Suite setup failed...Aborting suite %s" % self.__app.getName()) 00354 # return 00355 #else: 00356 self.__app.setSuite(self.getName()) 00357 self.__app.setSuiteInstance(self) 00358 self.__app.rcSetupSuite() 00359 self.__app.rcStartSuite(spawn=0) 00360 self.__isOKtoQuitOrPause.set() 00361 #self.__app.rcStopSuite() 00362 #self.__app = None 00363 else: 00364 errorMsg = '%s is not a recognized user script. userApplication or userSuite class missing' % module 00365 log.error(errorMsg) 00366 raise RuntimeError, errorMsg 00367 return self.__copySessionVars() 00368 00369 def resetSuiteScript(self): 00370 if self.__app.__class__.__name__ == 'userApplication' and not self.stopRequested(): 00371 self.__isOKtoQuitOrPause.clear() 00372 self.__reset = True 00373 if 'sync' in dir(self.__app): 00374 self.__app.sync() 00375 self.__app.rcStopRun() 00376 self.__app.rcTeardown() 00377 self.__isOKtoQuitOrPause.set() 00378 00379 def setCmdCli(self, cmdCli): 00380 self.__cmdCli = cmdCli 00381 00382 def setEvtCli(self, evtCli): 00383 self.__evtCli = evtCli 00384 00385 def getEvtCli(self): 00386 return self.__evtCli 00387 00388 def isSuiteScriptAborted(self): 00389 return self.__reset 00390 00391 def getScriptSequence(self): 00392 return self.__scriptSeq 00393 00394 def getScriptState(self): 00395 """\brief Return the state of the suite script. 00396 00397 Currently there are two states. 'RESET' or 'RUNNING'. 00398 If rcSetupSuite or rcStopSuite has completed execution then 00399 the state will be 'RESET'. If rcStartSuite started execution 00400 then the state will be 'RUNNING'. 00401 00402 \return 'RESET' or 'RUNNING' 00403 """ 00404 return self.__state 00405 00406 def getAppState(self): 00407 """\brief Return the state of the current script. 00408 00409 \return State of the current script 00410 """ 00411 return self.__appState 00412 00413 def getApp(self): 00414 """\brief Return the instance of current script. 00415 00416 Returns the instance for the suite script or nested suite 00417 that is currently executing. 00418 00419 \return userApplication or userSuite instance. 00420 """ 00421 return self.__app 00422 00423 def getSuite(self): 00424 """\brief Get the parent suite name of this suite 00425 00426 \return Parent suite name or None. 00427 """ 00428 return self.__suite 00429 00430 def setSuite(self, name): 00431 """\brief Set the parent suite name of this suite 00432 00433 \param name Parent suite name 00434 """ 00435 self.__suite = name 00436 00437 def getSuiteInstance(self): 00438 """\brief Return the parent suite instance that this suite is a part of. 00439 00440 \return Parent suite instance. 00441 """ 00442 return self.__suiteInst 00443 00444 def setSuiteInstance(self, suiteInst): 00445 """\brief Set the parent suite instance for this suite. 00446 00447 \param suiteInst Parent suite instance. 00448 """ 00449 self.__suiteInst = suiteInst 00450 00451 def setSessionVar(self, key, value): 00452 """\brief Set a session variable. 00453 00454 Do not use variable names beginning 00455 with the underscore ('_') character since these are reserved for RunControl 00456 """ 00457 sessionVars = self.__common.getDBN() 00458 if sessionVars is not None: 00459 sessionVars[key] = value 00460 else: 00461 raise RuntimeError, "Database node does not exist" 00462 00463 def getSessionVar(self, key): 00464 """Retrieve the values of the session variable indicated by \a key. 00465 00466 \return Session variable value 00467 """ 00468 sessionVars = self.__common.getDBN() 00469 if sessionVars is not None and sessionVars.has_key(key): 00470 return sessionVars[key] 00471 else: 00472 return None 00473 00474 def __copySessionVars(self): 00475 newCopy = {} 00476 vars = self.__common.getDBN() 00477 for varName in vars.keys(): 00478 if varName.startswith('_'): continue 00479 try: 00480 newCopy[varName] = copy.deepcopy(vars[varName]) 00481 except: 00482 newCopy[varName] = copy.copy(vars[varName]) 00483 return newCopy 00484 00485 #def readSchema(self, scmCfgFile): 00486 # fn = "rcTransitions.readSchema" 00487 # lat = gSchemaConfig.readSchema(scmCfgFile) 00488 # if lat is None: 00489 # log.error("%s: Unable to instatiate a LAT from SchemaConfig file '%s'" % \ 00490 # (fn, scmCfgFile)) 00491 # return None 00492 # includeFiles = lat._GLAT__includeFiles 00493 # incFileModTime = {} 00494 # for f in includeFiles: 00495 # incFileModTime[f] = os.path.getmtime(f) 00496 # lat.setCmd(self.__cmdCli) 00497 # self.setSessionVar('_LATSchema', lat) 00498 # self.setSessionVar('_LATSchemaFile', scmCfgFile) 00499 # self.setSessionVar('_LATSchemaFileModTime', os.path.getmtime(scmCfgFile)) 00500 # self.setSessionVar('_LATIncludeFileModTime', incFileModTime) 00501 # return lat 00502 00503 def readSchema(self, scmCfgFile): 00504 self.__common.readSchema(scmCfgFile) 00505 00506 def applyConfig(self, seq=None, gxbrd=None): 00507 if self.lat is not None: 00508 self.lat.applyConfig(seq, gxbrd) 00509 00510 def stopRequested(self): 00511 return self.__cmdQuit 00512 00513 def clearStopRequested(self): 00514 self.__cmdQuit = 0 00515 00516 def isPaused(self): 00517 return self.__paused 00518 00519 def isRunning(self): 00520 return self.__running 00521 00522 def getName(self): 00523 return self.__class__.__name__ 00524 00525 def startSuite(self): 00526 raise RuntimeError, "Suite does not define a startSuite method"