00001 #!/usr/local/bin/python 00002 # 00003 # Copyright 2004 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 finite state transistions classes" 00012 __author__ = "R. Claus <Claus@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online" 00013 __date__ = ("$Date: 2006/07/19 20:06:56 $").split(' ')[1] 00014 __version__ = "$Revision: 2.137 $" 00015 __release__ = "$Name: R04-12-00 $" 00016 __credits__ = "SLAC" 00017 00018 import LATTE.copyright_SLAC 00019 00020 from threading import Thread, Event, Lock 00021 import logging as log 00022 import os 00023 import time 00024 import sys 00025 import types 00026 import struct 00027 00028 import rcReportGen 00029 import rcRunIdHelper 00030 import rcHouseKeeping 00031 import rcFitsWriter 00032 import rcArchiver 00033 import LATsweep 00034 import rcComplStatus 00035 import rcTestReport 00036 import rcUtil 00037 from RunControlCommon import RunControlCommon 00038 00039 00040 from LATTE.tools import FreeSpace 00041 from LATTE.tools import DataExport 00042 from LATTE.trigger import rcDefaultTrigger 00043 from LATTE.client import gLog, gException 00044 from LATTE.database import gGLT 00045 from LATTE.client.gEvtCli import EvtCli 00046 from LATTE.database.gVersions import Gversions, ROOT_DIR_ENVS 00047 00048 class rcTransitions(object): 00049 "Class that handles the state machine transitions issued by Run Control" 00050 __SWEEP_MARKER = 5 # Arbitrary marker value for recognizing 00051 # when the event pipeline is flushed 00052 00053 COMPL_STATUS_UNDEFINED = \ 00054 rcComplStatus.rcCompletionStatus.getCompletionStatus('UNDEFINED') 00055 COMPL_STATUS_ABORTED = \ 00056 rcComplStatus.rcCompletionStatus.getCompletionStatus('ABORTED') 00057 COMPL_STATUS_ERROR = \ 00058 rcComplStatus.rcCompletionStatus.getCompletionStatus('FAILED') 00059 COMPL_STATUS_SUCCESS = \ 00060 rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED') 00061 COMPL_STATUS_FAILED = \ 00062 rcComplStatus.rcCompletionStatus.getCompletionStatus('FAILED') 00063 COMPL_STATUS_PASSED = \ 00064 rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED') 00065 COMPL_STATUS_PASSED_CONDITIONALLY = \ 00066 rcComplStatus.rcCompletionStatus.getCompletionStatus('PASSED CONDITIONALLY') 00067 COMPL_STATUS_MARGINAL = \ 00068 rcComplStatus.rcCompletionStatus.getCompletionStatus('MARGINAL') 00069 COMPL_STATUS_OPERATOR_ABORTED = \ 00070 rcComplStatus.rcCompletionStatus.getCompletionStatus('OPERATOR ABORTED') 00071 COMPL_STATUS_INCOMPLETE = \ 00072 rcComplStatus.rcCompletionStatus.getCompletionStatus('INCOMPLETE') 00073 00074 # Database node for storing session variables 00075 #__dbn = GDBN() 00076 00077 # Watermark for diskspace warning 00078 DISKSPACE_WARN_LEVEL = (200*1024*1024) 00079 00080 # Deadtime related constants 00081 DEADTIME_RATE_IN_MHZ = 20.0 00082 TIMEBASE_RATE_IN_MHZ = 16.6666667 00083 00084 EVENT_HANDLER_STANDARD = 0 00085 EVENT_HANDLER_LEAN_AND_MEAN = 1 00086 EVENT_HANDLER_PLAYBACK = 2 00087 EVENT_HANDLER_CUSTOM = 3 00088 00089 00090 def __init__(self, rc, userId, debug): 00091 fn = "rcTransitions.__init__" 00092 log.debug("%s -- %s" % (fn, self.getName())) 00093 self.__userId = userId 00094 00095 # Used in case the script is run in standalone mode 00096 if debug.__class__.__name__ == 'RunControlCommon': 00097 self.__common = debug 00098 self.__debug = None 00099 else: 00100 self.__debug = debug 00101 if rc is not None: 00102 self.__common = rc.common() 00103 else: 00104 raise RuntimeError, "Please supply a RunControlCommon object" 00105 00106 # The following data members are meant to be marked "protected" 00107 self.rc = rc 00108 self.gui = rc # For backward compatibility 00109 00110 self.__complStatus = rcComplStatus.rcCompletionStatus() 00111 00112 self.prefs = self.__common.prefMan().preferences() 00113 if self.__common.getLAT() is None: 00114 if rc is not None: 00115 rc.execGUImethod(rc.loadSchema) 00116 else: 00117 msg = "Please select a schema first" 00118 log.error(msg) 00119 raise RuntimeError, msg 00120 self.lat = self.__common.getLAT() 00121 self.xbrd = self.__common.getXBRD() 00122 self.ocs = self.__common.getOCS() 00123 self.evtCli = self.__common.getEvtCli() 00124 self.__cmdCli = self.__common.getCmdCli() 00125 00126 self.__trigger = None 00127 self.__archiver = None 00128 self.__archiverClass = None 00129 self.__fits = None 00130 self.__errArchiver = None 00131 self.__badArchiver = None 00132 self.__eventHandlerReady = Event() 00133 self.__sweepEvent = Event() 00134 self.__sweepEvent.set() 00135 self.__evtHandlerQuit = False 00136 00137 self.evtCnt = 0 00138 self.__SBCevtCnt = 0 00139 self.__pauseCount = 0 00140 self.__pausedTime = 0 00141 self.__pauseTime = None 00142 self.__startTime = None 00143 self.__strobeTime = None 00144 self.__elapsedTime = 0 00145 self.__endTime = None 00146 self.__excludedTime = 0 00147 self.__excludeTime = 0 00148 self.__excluding = False 00149 self.__excludeLock = Lock() 00150 self.__badEvents = 0 00151 self.__errEvents = 0 00152 self.__trgParErrorEvt = 0 00153 self.__pktErrorEvt = 0 00154 self.__hsk = None 00155 self.__tstamp = None 00156 self.__tstampRaw = None 00157 self.__deadtimeLock = Lock() 00158 self.__deadtimeCtrReg = {} 00159 self.__deadTimeVal = {} 00160 self.__deadTimeTS = {} 00161 self.__avgDeadTime = {} 00162 self.__statMon = None 00163 self.__dgSize = 0 00164 self.__dgSumSize = 0L 00165 self.__lastDgSumSize = 0 00166 self.__instByteRate = 0.0 00167 self.__lastEvtCnt = 0 00168 self.__lastSBCevtCnt = 0 00169 self.__lastTime = 0 00170 self.__lastSBCtime = 0 00171 self.__lastDgSumSize = 0 00172 self.__instByteRate = 0.0 00173 00174 # This will contain the parent suite name during execution 00175 self.__suite = None 00176 00177 # This will contain the parent suite instance during execution 00178 self.__suiteInst = None 00179 00180 # This will be True if it is the last script in the suite 00181 self.__lastSuiteScript = False 00182 00183 self.__EBFplayback = self.__common.options().playback is not None 00184 00185 self.__runIdHelper = rcRunIdHelper.rcRunIdHelper(self.prefs) 00186 self.runId = 'N/A' 00187 00188 self.__customReportData = {} 00189 00190 if rc is not None: 00191 self.__paramVerifier = self.rc.parameterVerifier() 00192 00193 self.__verifyStatus = None 00194 00195 self.__appStatus = None 00196 self.__completionStatus = self.COMPL_STATUS_UNDEFINED 00197 00198 self.__dds = self.__common.getDDS() 00199 00200 # Contains the full path for the run based message log 00201 self.__runMsgLogFile = None 00202 00203 # Contains a dictionary of snapshot results 00204 self.__snapResult = {} 00205 00206 if self.rc is not None and 'statusMonitor' in dir(rc): 00207 statMon = self.rc.statusMonitor() 00208 statMon.addWatchItem('Run Id', self.getRunId) 00209 statMon.addWatchItem('Test Name', self.getName) 00210 statMon.addWatchItem('Suite Name', self.getSuite) 00211 statMon.addWatchItem('Additional Input Files', self.getAddlInputFiles) 00212 statMon.addWatchItem('Completion Status', self.getCompletionStatusStr, 00213 alarmExpr="@!='" + self.__complStatus.getCompletionStatusStr(rcTransitions.COMPL_STATUS_PASSED)+"'") 00214 statMon.addWatchItem('Event Count', self.getEventCount) 00215 statMon.addWatchItem('TEM Error Events', self.getErrorEvents, alarmExpr='!=0') 00216 statMon.addWatchItem('Trg Parity Error Events', self.getTrgParityErrorEvents, alarmExpr='!=0') 00217 statMon.addWatchItem('Packet Error Events', self.getPacketErrorEvents, alarmExpr='!=0') 00218 statMon.addWatchItem('Bad SBC Events', self.getBadEvents, alarmExpr='!=0') 00219 statMon.addWatchItem('Start Time', self.getStartTime) 00220 statMon.addWatchItem('End Time', self.getEndTime) 00221 statMon.addWatchItem('Elapsed Time', self.getElapsedTime) 00222 statMon.addWatchItem('Average SBC Event Rate', self.getSBCEventRate) 00223 statMon.addWatchItem('Instant SBC Event Rate', self.getInstSBCEventRate) 00224 statMon.addWatchItem('Average Event Rate', self.getEventRate) 00225 statMon.addWatchItem('Instant Event Rate', self.getInstEventRate) 00226 statMon.addWatchItem('Average Datagram Size', self.getAvgDatagramSize) 00227 statMon.addWatchItem('Instant Datagram Size', self.getInstDatagramSize) 00228 #statMon.addWatchItem('Average Byte Rate', self.getAvgByteRate) 00229 statMon.addWatchItem('Instant Byte Rate', self.getInstByteRate) 00230 statMon.addWatchItem('Avg DeadTime', self.getAvgDeadTime, offBoard=True) 00231 statMon.addWatchItem('GEM DeadTime (Avg , Inst)', self.getGemDeadTime, offBoard=True) 00232 statMon.addWatchItem('Pause Count', self.getPauseCount) 00233 statMon.addWatchItem('Paused Time', self.getPausedTime) 00234 statMon.addWatchItem('Run Message Log File', self.getRunMsgLogFile) 00235 statMon.addWatchItem('Archive File', self.getArchiveFile) 00236 #statMon.addWatchItem('FITS File', self.getFITSfile) 00237 #statMon.addWatchItem('HSK File', self.getHSKfile) 00238 #statMon.addWatchItem('Bad SBC Events File', self.getBadEventsFile) 00239 #statMon.addWatchItem('TEM Error Events File', self.getErrorEventsFile) 00240 statMon.addWatchItem('SBC Handled Events', self.getSBCHandledEvents, offBoard=True) 00241 statMon.addWatchItem('Valid Events', self.getValidEvents, offBoard=True) 00242 statMon.addWatchItem('Failed Event TCP Sends', self.getFailedEventSends, alarmExpr='!=0', offBoard=True) 00243 statMon.addWatchItem('Failed Event H/W Xmits', self.getFailedEvents, alarmExpr='!=0', offBoard=True) 00244 statMon.addWatchItem('Discarded Events', self.getDiscardedEvents, alarmExpr='!=0', offBoard=True) 00245 statMon.addWatchItem('Length Mismatched Events', self.getLengthMismatchedEvents, alarmExpr='!=0', offBoard=True) 00246 statMon.addWatchItem('Unsent Events', self.getUnsentEvents, alarmExpr='!=0', offBoard=True) 00247 statMon.addWatchItem('Error Log Count', self.__common.getErrorLogCount, alarmExpr='!=0') 00248 statMon.addWatchItem('Free Disk Space', FreeSpace.FreeSpace, 00249 alarmExpr='<'+ str(rcTransitions.DISKSPACE_WARN_LEVEL)) 00250 self.__statMon = statMon 00251 00252 self.__reportGen = rcReportGen.rcReportGen(os.path.join(self.prefs["reportdir"], 'rcReport.out'), self.prefs) 00253 00254 try: 00255 self.setArchiverClass(rcArchiver.rcArchiver) 00256 except (ImportError, IOError), e: 00257 log.error("%s: Archiver disabled: %s" % (fn, e)) 00258 00259 if self.__common.options().noerrfile is not None: 00260 self.handleEventOutput = self.__handleEventOutputStub 00261 else: 00262 self.handleEventOutput = self.__handleEventOutput 00263 00264 def getFailedEvents(self): 00265 """\brief Return failed event count. 00266 00267 OES increments this counter when 00268 there is a packet transmission error. 00269 00270 \return Failed event count. 00271 """ 00272 if self.__cmdCli.isConnected(): 00273 try: 00274 saveDebug = self.__cmdCli.getDebug() 00275 self.__cmdCli.setDebug(0) 00276 cnt = self.ocs.stats_failed_events 00277 self.__cmdCli.setDebug(saveDebug) 00278 return cnt 00279 except: pass 00280 return 0 00281 00282 def getFailedEventSends(self): 00283 """\brief Return number of failed event sends. 00284 00285 OES increments this counter when there is a TCP 00286 transport problem between the SBC and the teststand. 00287 00288 \return Number of failed event sends. 00289 """ 00290 if self.__cmdCli.isConnected(): 00291 try: 00292 saveDebug = self.__cmdCli.getDebug() 00293 self.__cmdCli.setDebug(0) 00294 cnt = self.ocs.stats_failed_event_sends 00295 self.__cmdCli.setDebug(saveDebug) 00296 return cnt 00297 except: pass 00298 return 0 00299 00300 def getSBCHandledEvents(self): 00301 """\brief Return number of events handled by the SBC. 00302 00303 OES increments this counter when an event has been 00304 received by the event handler. 00305 00306 \return Number of events handled by the SBC. 00307 """ 00308 if self.__cmdCli.isConnected(): 00309 try: 00310 saveDebug = self.__cmdCli.getDebug() 00311 self.__cmdCli.setDebug(0) 00312 self.__SBCevtCnt = self.ocs.stats_handled_events 00313 self.__cmdCli.setDebug(saveDebug) 00314 return self.__SBCevtCnt 00315 except: pass 00316 return 0 00317 00318 def getValidEvents(self): 00319 """\brief Return number of events successfully sent. 00320 00321 OES increments this counter when an event has been 00322 successfully transmitted to the teststand. 00323 00324 \return Number of valid events sent. 00325 """ 00326 if self.__cmdCli.isConnected(): 00327 try: 00328 saveDebug = self.__cmdCli.getDebug() 00329 self.__cmdCli.setDebug(0) 00330 cnt = self.ocs.stats_valid_events 00331 self.__cmdCli.setDebug(saveDebug) 00332 return cnt 00333 except: pass 00334 return 0 00335 00336 def getDiscardedEvents(self): 00337 """\brief Return number of events discarded. 00338 00339 OES increments this counter when an event has been 00340 discarded. Currently this method always returns 0. 00341 00342 \return Number of events discarded. 00343 """ 00344 if self.__cmdCli.isConnected(): 00345 try: 00346 saveDebug = self.__cmdCli.getDebug() 00347 self.__cmdCli.setDebug(0) 00348 cnt = self.ocs.stats_discarded_event_sends 00349 self.__cmdCli.setDebug(saveDebug) 00350 return cnt 00351 except: pass 00352 return 0 00353 00354 def getLengthMismatchedEvents(self): 00355 """\brief Return number of length mismatched events. 00356 00357 OES increments this counter when an event size doesn't 00358 match the length specified in the event. 00359 Currently this method always returns 0. 00360 00361 \return Number of length mismatched events. 00362 """ 00363 if self.__cmdCli.isConnected(): 00364 try: 00365 saveDebug = self.__cmdCli.getDebug() 00366 self.__cmdCli.setDebug(0) 00367 cnt = self.ocs.stats_length_mismatch_count 00368 self.__cmdCli.setDebug(saveDebug) 00369 return cnt 00370 except: pass 00371 return 0 00372 00373 def getUnsentEvents(self): 00374 """\brief Return number of events filtered or prescaled. 00375 00376 OES increments this counter when there is a filter 00377 or prescale set that prevents the event from being 00378 transmitted. 00379 00380 \return Number of unsent events. 00381 """ 00382 if self.__cmdCli.isConnected(): 00383 try: 00384 saveDebug = self.__cmdCli.getDebug() 00385 self.__cmdCli.setDebug(0) 00386 cnt = self.ocs.stats_events_not_sent 00387 self.__cmdCli.setDebug(saveDebug) 00388 return cnt 00389 except: pass 00390 return 0 00391 00392 def getAvgDeadTime(self, temId=None): 00393 """\brief Return the average deadtime. 00394 00395 Returns the average deadtime as a percentage for a TEM specified by \a temId 00396 or all TEMs in the schema as a dictionary. 00397 00398 \return Average deadtime(s) 00399 """ 00400 if self.__cmdCli.isConnected(): 00401 self.__deadtimeLock.acquire() 00402 avgDeadTimes = self.__calcDeadTime(temId) 00403 self.__deadtimeLock.release() 00404 if temId is not None: 00405 if avgDeadTimes[temId] is not None: 00406 self.__avgDeadTime[temId] = "%3.3f" % avgDeadTimes[temId] 00407 else: 00408 self.__avgDeadTime[temId] = "N/A" 00409 return self.__avgDeadTime[temId] 00410 else: 00411 for (id, dt) in avgDeadTimes.items(): 00412 if dt is not None: 00413 self.__avgDeadTime[id] = "%3.3f" % dt 00414 else: 00415 self.__avgDeadTime[id] = "N/A" 00416 return self.__avgDeadTime 00417 00418 def getGemDeadTime(self): 00419 """\brief Return the Average GEM dead time as a percentage 00420 """ 00421 if self.__cmdCli.isConnected(): 00422 if not self.lat.existsGEM(): 00423 return ("N/A", "N/A") 00424 saveDebug = self.__cmdCli.getDebug() 00425 self.__deadtimeLock.acquire() 00426 if 'GEM' not in self.__deadTimeVal.keys() or \ 00427 'GEM' not in self.__deadTimeTS.keys() or \ 00428 self.__startTime is None: 00429 self.__deadtimeLock.release() 00430 return ("N/A", "N/A") 00431 gst = self.lat.GEM.GEMST 00432 self.__cmdCli.setDebug(0) 00433 try: 00434 val, ts = gst.extCtrRead(gst.regs['livetime'].id()).payloads() 00435 except gException.LATInterfaceException, e: 00436 if e.errstr()[0] == 'OCS_TMO': 00437 self.__deadtimeLock.release() 00438 return ("TO", "TO") 00439 else: 00440 raise 00441 00442 # need to remove excluded time from both numerator and denominator 00443 excludeTime = (time.time() - self.__startTime - self.__getElapsedTime()) * rcTransitions.DEADTIME_RATE_IN_MHZ * 1000000 00444 dTime = val - self.__deadTimeVal['GEM'] - excludeTime 00445 iDTime = val - self.__deadTimeVal['LASTGEM'] 00446 dTS = (ts - self.__deadTimeTS['GEM']) - excludeTime 00447 iDTS = (ts - self.__deadTimeTS['LASTGEM']) 00448 self.__deadtimeLock.release() 00449 self.__cmdCli.setDebug(saveDebug) 00450 00451 if dTS != 0: 00452 avgLiveTime = (float(dTime)/float(dTS)) * 100.0 00453 else: 00454 avgLiveTime = None 00455 return ("N/A", "N/A") 00456 00457 if iDTS != 0: 00458 instLiveTime = (float(iDTime)/float(iDTS)) * 100.0 00459 else: 00460 instLiveTime = 0.0 00461 00462 self.__deadTimeVal['LASTGEM'] = val 00463 self.__deadTimeTS['LASTGEM'] = ts 00464 00465 return ("%3.3f" % (100.0 - avgLiveTime), "%3.3f" % (100.0 - instLiveTime)) 00466 00467 00468 def getRunId(self): 00469 """\brief Return the run id 00470 00471 \return Run id 00472 """ 00473 return self.runId 00474 00475 def getSchemaConfigFile(self): 00476 """\brief Return the full path of the schema file 00477 00478 \return Schema file path 00479 """ 00480 return self.getSessionVar('_LATSchemaFile') 00481 00482 def getAddlInputFiles(self): 00483 """\brief Return a list containing the additional input files. 00484 00485 This list is automatically generated from the XML files included 00486 in the schema using the <include> tag. The list contains only the 00487 filename portion and doesn't include the full path. 00488 00489 \return List of additional input files. 00490 """ 00491 if self.lat is not None: 00492 return [f for (p,f) in map(os.path.split, self.lat._GLAT__includeFiles)] 00493 00494 def getRunMsgLogFile(self): 00495 """\brief Return the filename for the message log for the current run. 00496 00497 \return Message log filename. 00498 """ 00499 if self.__runMsgLogFile is not None: 00500 return os.path.basename(self.__runMsgLogFile) 00501 else: 00502 return None 00503 00504 def getArchiveFile(self): 00505 """\brief Return the filename for the archive file (LDF file). 00506 00507 \return Archive filename. 00508 """ 00509 if self.__archiver is not None: 00510 return self.__archiver.getFileName() 00511 else: 00512 return None 00513 00514 def getBadEventsFile(self): 00515 """\brief Return the filename for the bad events archive. 00516 00517 \return Bad events archive filename. 00518 """ 00519 if self.__badArchiver is not None: 00520 return self.__badArchiver.getFileName() 00521 else: 00522 return None 00523 00524 def getErrorEventsFile(self): 00525 """\brief Return the filename for the error events archive. 00526 00527 \return Error events archive filename. 00528 """ 00529 if self.__errArchiver is not None: 00530 return self.__errArchiver.getFileName() 00531 else: 00532 return None 00533 00534 def getFITSfile(self): 00535 """\brief Return the filename for the FITS file. 00536 00537 \return FITS filename. 00538 """ 00539 if self.__fits is not None: 00540 return self.__fits.getFileName() 00541 else: 00542 return None 00543 00544 def getHSKfile(self): 00545 """\brief Return the filename for the housekeeping FITS file. 00546 00547 \return Housekeeping FITS filename. 00548 """ 00549 if self.__hsk is not None: 00550 return self.__hsk.getFileName() 00551 else: 00552 return None 00553 00554 00555 def getPauseCount(self): 00556 """\brief Return number of times the test is paused. 00557 00558 \return Pause count 00559 """ 00560 return self.__pauseCount 00561 00562 def getPausedTime(self): 00563 """\brief Return the amount of time spent in pause. 00564 00565 \return Pause time. 00566 """ 00567 if self.__pauseTime is not None: 00568 return "%.2f seconds" % (self.__pausedTime + (time.time() - self.__pauseTime)) 00569 else: 00570 return "%.2f seconds" % self.__pausedTime 00571 00572 def getEventCount(self): 00573 """\brief Return number of events received. 00574 00575 \return Event count 00576 """ 00577 return self.evtCnt 00578 00579 def getElapsedTime(self): 00580 """\brief Return the elapsed time spent during test execution. 00581 00582 \return Elapsed time. 00583 """ 00584 return "%.2f seconds" % self.__getElapsedTime() 00585 00586 def __getElapsedTime(self): 00587 self.__excludeLock.acquire() 00588 if self.__strobeTime is not None and \ 00589 self.__pauseTime is None and not self.__excluding: 00590 elapsedTime = self.__elapsedTime + (time.time() - self.__strobeTime) 00591 else: 00592 elapsedTime = self.__elapsedTime 00593 self.__excludeLock.release() 00594 return elapsedTime 00595 00596 00597 def getEventRate(self): 00598 """\brief Return the event rate. 00599 00600 \return Event rate. 00601 """ 00602 self.__excludeLock.acquire() 00603 if self.__strobeTime is not None and \ 00604 self.__pauseTime is None and not self.__excluding: 00605 elapsedTime = self.__elapsedTime + (time.time() - self.__strobeTime) 00606 if elapsedTime > 0: 00607 msg = "%.2f events/sec" % (self.evtCnt / elapsedTime) 00608 else: 00609 msg = "N/A" 00610 elif self.__elapsedTime == 0: 00611 msg = "N/A" 00612 else: 00613 msg = "%.2f events/sec" % (self.evtCnt / (self.__elapsedTime)) 00614 self.__excludeLock.release() 00615 return msg 00616 00617 def getSBCEventRate(self): 00618 """\brief Return the SBC event handling rate. 00619 00620 \return SBC event handling rate. 00621 """ 00622 self.__excludeLock.acquire() 00623 if self.__strobeTime is not None and \ 00624 self.__pauseTime is None and not self.__excluding: 00625 elapsedTime = self.__elapsedTime + (time.time() - self.__strobeTime) 00626 if elapsedTime > 0: 00627 msg = "%.2f events/sec" % (self.__SBCevtCnt / elapsedTime) 00628 else: 00629 msg = "N/A" 00630 elif self.__elapsedTime == 0: 00631 msg = "N/A" 00632 else: 00633 msg = "%.2f events/sec" % (self.__SBCevtCnt / (self.__elapsedTime)) 00634 self.__excludeLock.release() 00635 return msg 00636 00637 def getInstEventRate(self): 00638 """\brief Return the instant event rate. 00639 00640 \return Instant event rate. 00641 """ 00642 self.__excludeLock.acquire() 00643 evtCnt = self.evtCnt 00644 dC = evtCnt - self.__lastEvtCnt 00645 self.__lastEvtCnt = evtCnt 00646 00647 dgSumSize = self.__dgSumSize 00648 dS = dgSumSize - self.__lastDgSumSize 00649 self.__lastDgSumSize = dgSumSize 00650 00651 now = time.time() 00652 dT = now - self.__lastTime 00653 self.__lastTime = now 00654 00655 if dT == 0: dT = .00001 00656 msg = "%.2f events/sec" % (float(dC) / dT) 00657 self.__instByteRate = float(dS) / dT 00658 self.__excludeLock.release() 00659 return msg 00660 00661 def getInstSBCEventRate(self): 00662 """\brief Return the instant SBC event handling rate. 00663 00664 \return Instant SBC event handling rate. 00665 """ 00666 self.__excludeLock.acquire() 00667 evtCnt = self.__SBCevtCnt 00668 dC = evtCnt - self.__lastSBCevtCnt 00669 self.__lastSBCevtCnt = evtCnt 00670 00671 now = time.time() 00672 dT = now - self.__lastSBCtime 00673 self.__lastSBCtime = now 00674 00675 if dT == 0: dT = .00001 00676 msg = "%.2f events/sec" % (float(dC) / dT) 00677 self.__excludeLock.release() 00678 return msg 00679 00680 def getInstDatagramSize(self): 00681 """\brief Return the instant datagram size. 00682 00683 \return Instant datagram size. 00684 """ 00685 self.__excludeLock.acquire() 00686 msg = str(self.__dgSize) 00687 self.__excludeLock.release() 00688 return msg 00689 00690 def getAvgDatagramSize(self): 00691 """\brief Return the instant datagram size divided by the number of events. 00692 00693 \return Average datagram size 00694 """ 00695 self.__excludeLock.acquire() 00696 if self.evtCnt: 00697 msg = str(float(self.__dgSumSize) / float(self.evtCnt)) 00698 else: 00699 msg = "N/A" 00700 self.__excludeLock.release() 00701 return msg 00702 00703 def getInstByteRate(self): 00704 """\brief Return the instant datagram byte rate 00705 00706 \return Average datagram byte rate 00707 """ 00708 return "%.2f Kbytes/sec" % (self.__instByteRate / 1024) 00709 00710 def getStartTime(self): 00711 """\brief Return the test start time 00712 00713 \return Test start time in GMT as a tuple 00714 """ 00715 if self.__startTime is not None: 00716 return time.asctime(time.gmtime(self.__startTime)) 00717 else: 00718 return None 00719 00720 def getEndTime(self): 00721 """\brief Return the test end time 00722 00723 \return Test end time in GMT as a tuple 00724 """ 00725 if self.__endTime is not None: 00726 return time.asctime(time.gmtime(self.__endTime)) 00727 else: 00728 return None 00729 00730 def getBadEvents(self): 00731 """\brief Return number of events with a bad GGLT status 00732 00733 \return Number of bad events 00734 """ 00735 return self.__badEvents 00736 00737 def getErrorEvents(self): 00738 """\brief Return number of error events 00739 00740 \return Number of error events 00741 """ 00742 return self.__errEvents 00743 00744 def getTrgParityErrorEvents(self): 00745 """\brief Return number of trigger parity error events 00746 00747 \return Number of trigger parity error events 00748 """ 00749 return self.__trgParErrorEvt 00750 00751 def getPacketErrorEvents(self): 00752 """\brief Return number of packet error events 00753 00754 \return Number of packet error events 00755 """ 00756 return self.__pktErrorEvt 00757 00758 00759 def setCmdCli(self, cmdCli): 00760 """\brief Set the command client instance (used in suites) 00761 00762 \param A CmdCli instance 00763 """ 00764 self.cmdCli = cmdCli 00765 00766 def setEvtCli(self, evtCli): 00767 """\brief Set the event client instance. 00768 00769 Used to set the custom event client. 00770 00771 \param A CmdCli instance 00772 """ 00773 log.debug("Event client set to: %s" % evtCli.__class__.__name__) 00774 evtCli.setSocket(self.evtCli.socket()) 00775 self.evtCli = evtCli 00776 00777 def getEvtCli(self): 00778 """\brief Return the current event client instance. 00779 00780 \return An EvtCli instance 00781 """ 00782 return self.evtCli 00783 00784 def getDataDistributor(self): 00785 """\brief Return the data distribution server instance. 00786 00787 \return Data distribution server instance. 00788 """ 00789 return self.__dds 00790 00791 def setSessionVar(self, key, value): 00792 """\brief Set session variable \a key to value \a value. 00793 00794 Session variables are data that is shared between different runs. 00795 They exist as long as the run control session they are defined in. 00796 Typical uses of session variables are to pass data from one suite 00797 script to another or to maintain state during a multiple step 00798 test run. 00799 00800 \param key Session variable identifier. 00801 \param value Session variable value. 00802 """ 00803 sessionVars = self.__common.getDBN() 00804 if sessionVars is not None: 00805 sessionVars[key] = value 00806 else: 00807 raise RuntimeError, "Database node does not exist" 00808 00809 def getSessionVar(self, key): 00810 """\brief Return the session variable identified by \a key. 00811 00812 \param key Session variable identifier. 00813 00814 \return Value of the session variable if it exists, 00815 otherwise None. 00816 """ 00817 sessionVars = self.__common.getDBN() 00818 if sessionVars is not None and sessionVars.has_key(key): 00819 return sessionVars[key] 00820 else: 00821 return None 00822 00823 def setPrefs(self, prefs): 00824 """\brief Explicitly set preferences. 00825 00826 When scripts are run in standalone mode it may be 00827 necessary to pass in a script defined set of preferences. 00828 00829 The preferences specified in \a prefs get appended to 00830 the set of preferences read from the configuration file. 00831 00832 \param prefs Preferences as a dictionary. 00833 """ 00834 for (key, val) in prefs.items(): 00835 self.prefs[key] = val 00836 00837 def setCompletionStatus(self, completionStatus, completionStatusStr=None): 00838 """\brief Sets the completion status of the test. 00839 00840 Please use one of the following: 00841 00842 - COMPL_STATUS_ABORTED = -2 00843 - COMPL_STATUS_ERROR = -3 00844 - COMPL_STATUS_FAILED = -3 00845 - COMPL_STATUS_SUCCESS = 0 00846 - COMPL_STATUS_PASSED = 0 00847 - COMPL_STATUS_PASSED_CONDITIONALLY = -4 00848 - COMPL_STATUS_MARGINAL = -5 00849 00850 Or a user defined completion status in which case 00851 a description needs to be supplied along with the code. 00852 00853 \param completionStatus Numeric completion status code 00854 \param completionStatusStr Completion status description 00855 00856 """ 00857 if not self.__complStatus.isCompletionStatus(completionStatus): 00858 if completionStatusStr is None: 00859 raise RuntimeError, "Need to specify a completion status description " \ 00860 "when specifying a user defined completion status" 00861 else: 00862 addRes = self.__complStatus.addCompletionStatus(completionStatus, completionStatusStr) 00863 if addRes == -2: 00864 raise RuntimeError, "Completion status description '%s' already exists " \ 00865 "under a different status code." % completionStatusStr 00866 self.__completionStatus = completionStatus 00867 00868 def getCompletionStatus(self): 00869 """\brief Return the completion status code. 00870 00871 Returns the code set by the script or 00872 COMPL_STATUS_UNDEFINED if it hasn't been set. 00873 00874 \return Completion status code. 00875 """ 00876 if self.__appStatus is None: 00877 return self.__completionStatus 00878 else: 00879 return self.__appStatus 00880 00881 def getCompletionStatusStr(self): 00882 """\brief Return the description of the completion status. 00883 00884 Returns the code as set by the script or 00885 'UNDEFINED' if it hasn't been set. 00886 00887 \return Completion status description. 00888 """ 00889 completionStatus = self.getCompletionStatus() 00890 return self.__complStatus.getCompletionStatusStr(completionStatus) 00891 #if completionStatus == rcTransitions.COMPL_STATUS_ABORTED: 00892 # return("ABORTED") 00893 #elif completionStatus == rcTransitions.COMPL_STATUS_ERROR: 00894 # return("ERROR") 00895 #elif completionStatus == rcTransitions.COMPL_STATUS_SUCCESS: 00896 # return("SUCCESS") 00897 #else: 00898 # return("UNDEFINED") 00899 00900 def getSnapshotStatus(self, snapId=None): 00901 """\brief Return snapshot result(s). 00902 00903 Returns a dictionary of snapshot results or if snapId is specified, 00904 returns the corresponding snapshot status. 00905 00906 \param snapId Optional snapshot id. 00907 00908 \return Snapshot status as a boolean or status dictionary. 00909 None, if a snapshot with an id of \a snapId doesn't exist. 00910 """ 00911 if snapId is None: 00912 return self.__snapResult 00913 elif snapId in self.__snapResult: 00914 return self.__snapResult[snapId] 00915 else: 00916 return None 00917 00918 def __isTestComplete(self): 00919 if self.__common.options().securedir is not None: 00920 requiredOptions = ['snapnbl', 'datasave'] 00921 complete = True 00922 for option in requiredOptions: 00923 if self.prefs[option] != 1: 00924 complete = False 00925 break 00926 return complete 00927 else: 00928 return True 00929 00930 def getHSK(self): 00931 """ 00932 \brief Return the housekeeping class instance. 00933 00934 \return Housekeeping class instance 00935 """ 00936 return self.__hsk 00937 00938 def addCustomReportData(self, key, value): 00939 """\brief Add custom report data to the run report. 00940 00941 This method allows the application to add any additional data 00942 that Run Control is not already providing to the run report 00943 (rcReport.out). 00944 00945 When the custom report data gets processed and written to the 00946 database, it will go under the "AdditionFields" column. Please 00947 only provide values that can be converted to a string. 00948 00949 \param key Custom report data identifier. 00950 \param value Custom report data value. 00951 """ 00952 self.__customReportData[key] = value 00953 00954 def getParameterVerifier(self): 00955 """\brief Return the parameter verifier instance 00956 00957 \return Parameter verifier instance 00958 """ 00959 return self.__paramVerifier 00960 00961 def addExportedFile(self,file=None,delete=False): 00962 """\brief Add file to the list of files to be exported. 00963 00964 \param file file to be added 00965 \param delete delete original on export (default False) 00966 """ 00967 if file is not None: 00968 (filePath,fileName) = os.path.split(file) 00969 self.__exportedFiles.append( [filePath,fileName,delete] ) 00970 00971 # Try to extract the report file from the rcTestReport instance 00972 # that the script may have been using. 00973 if self.__testReport is None: 00974 for objStr in dir(self): 00975 obj = getattr(self, objStr) 00976 if '__class__' in dir(obj): 00977 if rcTestReport.rcTestReport in obj.__class__.__mro__: 00978 self.__testReport = obj 00979 break 00980 if self.__testReport is not None and file in self.__testReport.getReports(): 00981 self.__testReportFile = fileName 00982 00983 00984 def getFileTimeStamp(self): 00985 """\brief Return the formatted timestamp used to name output files. 00986 00987 Using this method, applications can request the formatted timestamp 00988 value used in naming archive, snapshot and housekeeping files. 00989 The format is YYMMDDHHMISS. 00990 00991 \return Formatted GMT timestamp 00992 """ 00993 return self.__tstamp 00994 00995 def getRawTimeStamp(self): 00996 """\brief Return the raw timestamp as returned from gmtime(). 00997 00998 \return Raw GMT timestamp tuple. 00999 """ 01000 return self.__tstampRaw 01001 01002 def getSuite(self): 01003 """\brief Return the name of the suite that this script is a part of. 01004 01005 \return Suite name. 01006 """ 01007 return self.__suite 01008 01009 def setSuite(self, suite): 01010 """\brief Set name of the suite for this script. 01011 01012 Called from rcSuite. 01013 01014 \param suite Name of the suite 01015 """ 01016 self.__suite = suite 01017 01018 def getSuiteInstance(self): 01019 """\brief Return the suite instance that this script is a part of. 01020 01021 \return Suite instance. 01022 """ 01023 return self.__suiteInst 01024 01025 def setSuiteInstance(self, suiteInst): 01026 """\brief Set the suite instance for this script. 01027 01028 Called from rcSuite. 01029 01030 \param suiteInst Suite instance. 01031 """ 01032 self.__suiteInst = suiteInst 01033 01034 def setLastSuiteScript(self): 01035 """\brief Set the script as the last one in the suite. 01036 01037 Called from rcSuite. 01038 """ 01039 self.__lastSuiteScript = True 01040 01041 def isLastSuiteScript(self): 01042 """\brief Check if the script is the last one. 01043 01044 """ 01045 return self.__lastSuiteScript 01046 01047 def getState(self): 01048 """\brief Return the state of the running script. 01049 01050 If the script is running as part of a suite then the script 01051 state is simulated. 01052 01053 \return Script state 01054 """ 01055 if self.isRunFromSuite(): 01056 return self.getSuiteInstance().getAppState() 01057 elif self.rc is not None: 01058 return self.rc.getFSM().current_state 01059 01060 01061 def trigger(self, trg=None, replaceShut=True): 01062 """\brief Return the trigger used in this script or 01063 initialize the trigger. 01064 01065 \param trg Trigger object. 01066 \param replaceShut Flag to specify whether the trigger's default 01067 shut logic should be wrapped by Run Control's 01068 LATsweep shut logic. Default is True. 01069 01070 \return Trigger object. 01071 """ 01072 if trg is not None: 01073 self.__trigger = trg 01074 if replaceShut: 01075 # Don't cache the shut call more than once. Otherwise you get recursion. 01076 if "_shut" not in dir(self.__trigger): 01077 self.__trigger._shut = self.__trigger.shut 01078 latSweep = LATsweep.LATsweep(self.lat, self.__trigger, self.__trigger._shut) 01079 self.__trigger.shut = latSweep.shut 01080 return self.__trigger 01081 01082 def isRunFromSuite(self): 01083 """\brief Check if the script is being run from a suite. 01084 01085 \return True If the script is being run from a suite, 01086 False otherwise. 01087 """ 01088 return self.__suite is not None 01089 01090 def __genReport(self): 01091 log.debug("Generating run report...") 01092 self.__reportGen.initialize(self.__tstampRaw) 01093 self.__reportGen.addField("TestName", self.getName()) 01094 if self.__suite is not None: 01095 self.__reportGen.addField("SuiteName", self.__suite) 01096 self.__reportGen.addField("RunId", self.runId) 01097 if self.prefs.has_key("operator"): 01098 self.__reportGen.addField("Operator", self.prefs["operator"]) 01099 if self.prefs.has_key("userid"): 01100 self.__reportGen.addField("OperatorId", self.prefs["userid"], rcReportGen.UNITS_LONG) 01101 self.__reportGen.addField("EventCount", str(self.evtCnt), rcReportGen.UNITS_LONG) 01102 self.__reportGen.addField("BadEventCount", str(self.__badEvents), rcReportGen.UNITS_LONG) 01103 self.__reportGen.addField("ErrorEventCount", str(self.__errEvents), rcReportGen.UNITS_LONG) 01104 self.__reportGen.addField("TriggerParityErrorEventCount", str(self.__trgParErrorEvt), rcReportGen.UNITS_LONG) 01105 self.__reportGen.addField("PacketErrorEventCount", str(self.__pktErrorEvt), rcReportGen.UNITS_LONG) 01106 self.__reportGen.addField("PauseCount", str(self.__pauseCount), rcReportGen.UNITS_INT) 01107 self.__reportGen.addField("PausedTime", str(self.__pausedTime), rcReportGen.UNITS_FLOAT) 01108 self.__reportGen.addField("StartTime", str(time.gmtime(self.__startTime))) 01109 self.__reportGen.addField("ElapsedTime", str(self.__elapsedTime), rcReportGen.UNITS_FLOAT) 01110 self.__reportGen.addField("EndTime", str(time.gmtime(self.__endTime))) 01111 self.__reportGen.addField("SBCHandledEvents", str(self.getSBCHandledEvents()), rcReportGen.UNITS_LONG) 01112 self.__reportGen.addField("ValidEvents", str(self.getValidEvents()), rcReportGen.UNITS_LONG) 01113 self.__reportGen.addField("FailedEventTCPSends", str(self.getFailedEventSends()), rcReportGen.UNITS_LONG) 01114 self.__reportGen.addField("FailedEventHWXmits", str(self.getFailedEvents()), rcReportGen.UNITS_LONG) 01115 self.__reportGen.addField("DiscardedEvents", str(self.getDiscardedEvents()), rcReportGen.UNITS_LONG) 01116 self.__reportGen.addField("LengthMismatchedEvents", str(self.getLengthMismatchedEvents()), rcReportGen.UNITS_LONG) 01117 self.__reportGen.addField("UnsentEvents", str(self.getUnsentEvents()), rcReportGen.UNITS_LONG) 01118 self.__reportGen.addField("FreeDiskSpace", str(FreeSpace.FreeSpace()), rcReportGen.UNITS_LONG) 01119 self.__reportGen.addField("SchemaConfigFile", os.path.split(self.__common.getDBN()['_LATSchemaFile'])[1]) 01120 self.__reportGen.addField("AdditionalInputFiles", str(self.getAddlInputFiles())) 01121 self.__reportGen.addField("AssociatedFiles", str([exp[1] for exp in self.__exportedFiles])) 01122 self.__reportGen.addField("CallbackParameters", str(self.__common.getOCS().getCallbackParams())) 01123 01124 releases = {} 01125 failed = [] 01126 failedNoVersion = [] 01127 01128 if int(self.prefs["versionnbl"]) == 1: 01129 try: 01130 self.__waitCursor() 01131 versions = Gversions.getModuleVersions(self.ocs) 01132 for (hwComp, hwVer) in self.__common.getHardwareVersions().items(): 01133 if hwComp in versions: 01134 log.warning("Hardware component %s with version %s conflicts with a module of same name" % 01135 (hwComp, hwVer) 01136 ) 01137 else: 01138 versions[hwComp] = hwVer 01139 01140 for (module, version) in versions.items(): 01141 if version.strip() == '': 01142 failedNoVersion.append(module) 01143 failed.append(module) 01144 self.__normalCursor() 01145 except: 01146 log.exception("Error in retrieving version information. Setting VersionData to an empty list.") 01147 versions = {} 01148 self.__reportGen.addField("VersionData", str(versions)) 01149 01150 if int(self.prefs["disableverification"]) == 0: 01151 try: 01152 filesToVerify= [self.__common.getDBN()['_LATSchemaFile']] + self.lat._GLAT__includeFiles 01153 if os.environ.has_key("VXW_ROOT"): 01154 filesToVerify.append(os.path.join(os.environ["VXW_ROOT"], "*.vx")) 01155 filesToVerify.append(os.path.join(os.environ["VXW_ROOT"], "lib/mv2304/*.o")) 01156 for env in ROOT_DIR_ENVS: 01157 if os.environ.has_key(env): 01158 release = Gversions.verifyModuleDigests(digestDataFile=None, rootDirEnv=env, 01159 additionalFilesToVerify=filesToVerify) 01160 subsys = env[:env.find("_ROOT")] 01161 releases[subsys] = release[0] 01162 for module in release[1]: 01163 if module not in failed: 01164 failed.append(module) 01165 noVersionInRelease = False 01166 for module in failedNoVersion: 01167 if module in release[1]: 01168 noVersionInRelease = True 01169 break 01170 if len(release[1]) > 0 or noVersionInRelease: 01171 log.error("Some modules failed verification for %s release %s. Check Elogbook for the modules that failed." % (subsys, release[0])) 01172 except: 01173 log.exception("Error in verification. Setting Release and ModulesFailedVerification to empty lists.") 01174 releases = {} 01175 failed = [] 01176 self.__reportGen.addField("Release", str(releases)) 01177 self.__reportGen.addField("ModulesFailedVerification", str(failed)) 01178 01179 if self.__fits is not None: 01180 self.__reportGen.addField("FITSfile", self.__fits.getFileName()) 01181 if self.__archiver is not None and self.__archiver.getFileName() is not None: 01182 self.__reportGen.addField("ArchiveFile", self.__archiver.getFileName()) 01183 if self.__errArchiver is not None and self.__errArchiver.getFileName() is not None: 01184 self.__reportGen.addField("ErrorArchive", self.__errArchiver.getFileName()) 01185 if self.__badArchiver is not None and self.__badArchiver.getFileName() is not None: 01186 self.__reportGen.addField("BadEventArchive", self.__badArchiver.getFileName()) 01187 # Change as per LTE-193 01188 #hskOutputEnabled = (int(self.prefs["envmonnbl"]) == 1) 01189 hskOutputEnabled = False 01190 # Change as per LTE-193 01191 #hskReadOnly = (int(self.prefs["envmonreadonly"]) == 1) 01192 hskReadOnly = False 01193 if hskOutputEnabled and self.__hsk is not None and not hskReadOnly: 01194 self.__reportGen.addField("HSKArchive", self.__hsk.getFileName()) 01195 if self.prefs.has_key("logfilename"): 01196 self.__reportGen.addField("LogFile", self.prefs["logfilename"]) 01197 if self.getRunMsgLogFile() is not None: 01198 self.__reportGen.addField("RunLogFile", self.getRunMsgLogFile()) 01199 comments = [] 01200 if self.rc is not None: 01201 cw = self.rc.getCommentWidget() 01202 if cw is not None: 01203 hist = cw.getHistory() 01204 tstamp = cw.getTimestamps() 01205 comments += zip(tstamp, hist) 01206 cw = self.rc.getEndRunComments() 01207 if cw is not None: 01208 hist = cw.getHistory() 01209 tstamp = cw.getTimestamps() 01210 comments += zip(tstamp, hist) 01211 self.__reportGen.addField("Comments", str(comments)) 01212 for (key, value) in self.__customReportData.items(): 01213 self.__reportGen.addField(key, str(value)) 01214 if self.prefs is not None: 01215 prefs = self.prefs 01216 if prefs.has_key("lastsite"): 01217 self.__reportGen.addField("Site", prefs["lastsite"]) 01218 if prefs.has_key("lastparticletype"): 01219 self.__reportGen.addField("ParticleType", prefs["lastparticletype"]) 01220 if prefs.has_key("lastinstrumenttype"): 01221 self.__reportGen.addField("InstrumentType", prefs["lastinstrumenttype"]) 01222 if prefs.has_key("lastorientation"): 01223 self.__reportGen.addField("Orientation", prefs["lastorientation"]) 01224 if prefs.has_key("lastphase"): 01225 self.__reportGen.addField("Phase", prefs["lastphase"]) 01226 self.__reportGen.addField("AverageDeadTime", str(self.getAvgDeadTime())) 01227 self.__reportGen.addField("GEMDeadTime", str(self.getGemDeadTime())) 01228 if self.__verifyStatus is not None: 01229 self.__reportGen.addField("ParamVerify", str(self.__verifyStatus)) 01230 allSerials = self.lat.getAllSerialNos() 01231 repSerials = {} 01232 if allSerials != {}: 01233 for (node, serialnos) in allSerials.items(): 01234 strNode = "%s%s" % (node.getName(), node.getIdTuple()) 01235 repSerials[strNode] = serialnos 01236 self.__reportGen.addField("SerialNos", str(repSerials)) 01237 if self.__testReportFile is not None: 01238 self.__reportGen.addField("ScriptReport", self.__testReportFile) 01239 01240 tc = self.trigger().conditions() 01241 tList = [] 01242 if tc.roi(): tList.append("roi") 01243 if tc.calLow(): tList.append("calLow") 01244 if tc.calHigh(): tList.append("calHigh") 01245 if tc.tkr(): tList.append("tkr") 01246 if tc.periodic(): tList.append("periodic") 01247 if tc.solicited(): tList.append("solicited") 01248 if tc.cno(): tList.append("cno") 01249 if tc.external(): tList.append("external") 01250 self.__reportGen.addField("TriggerMask", str(tList)) 01251 01252 errorLogCount = self.__common.getErrorLogCount() 01253 self.__reportGen.addField("ErrorLogCount", str(errorLogCount), rcReportGen.UNITS_INT) 01254 # If there are any error logs during the run override the status as FAILED 01255 if self.getCompletionStatus() == self.COMPL_STATUS_PASSED and errorLogCount != 0: 01256 log.warn("Errors were encountered during the run. Overriding the completion status from %s to FAILED." % (self.getCompletionStatusStr())) 01257 self.__appStatus = self.COMPL_STATUS_FAILED 01258 # As per Eduardo's request override the PASSED completion status to be 01259 # INCOMPLETE if any of his criteria (see __isTestComplete) aren't met 01260 elif self.getCompletionStatus() == self.COMPL_STATUS_PASSED and \ 01261 not self.__isTestComplete(): 01262 log.warn("Test was not complete. Overriding the completion status as INCOMPLETE.") 01263 self.__appStatus = self.COMPL_STATUS_INCOMPLETE 01264 01265 self.__reportGen.addField("CompletionStatus", str(self.getCompletionStatus()), rcReportGen.UNITS_INT) 01266 self.__reportGen.addField("CompletionStatusStr", self.getCompletionStatusStr()) 01267 01268 # We're done! 01269 self.__reportGen.writeReportXML() 01270 log.debug("Run report generation complete.") 01271 01272 def takeSnapshot(self, stream=sys.stdout, fileName=None, 01273 captureGTIC=1, captureGAEQ=1, 01274 trigger=None, 01275 configName='', configVersion=''): 01276 """\brief Take a snapshot of the hardware. 01277 01278 If snapshots are enabled in Run Control preferences, 01279 two snapshots will be taken, one before and one after 01280 the test has been run. This method allows the application 01281 to take a snapshot at a specific point of the run. 01282 01283 It is the application's responsibility to add the 01284 resulting file to the list of exported files by calling 01285 the addExportedFile method. 01286 01287 Since snapshots essentially have the same format as 01288 configuration blocks, configName and configVersion parameters 01289 can be used to output a named configuration. 01290 01291 \param stream Output stream for the snapshot. 01292 sys.stdout by default. 01293 \param fileName Full path of the snapshot file. 01294 \param captureGTIC Capture TEM environmental quantities. 01295 Enabled by default. 01296 \param captureGAEQ Capture AEM environmental quantities. 01297 Enabled by default. 01298 \param trigger Trigger object. If miniGLT is used as a 01299 trigger then GLT registers can be snapshot. 01300 \param configName Configuration name 01301 \param configVersion Configuration version 01302 01303 \return True if the snapshot was successful, 01304 False if errors were encountered during snapshot 01305 """ 01306 01307 if self.__statMon is not None: 01308 self.rc.execGUImethod(self.__statMon.stopWatch) 01309 01310 result = self.lat.takeSnapshot(stream, fileName, 01311 captureGTIC, captureGAEQ, 01312 trigger, configName, configVersion) 01313 01314 if self.__statMon is not None: 01315 self.rc.execGUImethod(self.__statMon.startWatch) 01316 01317 return result 01318 01319 def readConfig(self, cfgFileName): 01320 """\brief Read a configuration XML file. 01321 01322 \param cfgFileName Full path of the configuration file. 01323 01324 \return Sequence number for this configuration. This number 01325 can later be passed to applyConfig. 01326 """ 01327 seq = self.lat.readConfig(cfgFileName) 01328 if seq != -1 and cfgFileName not in self.lat._GLAT__includeFiles: 01329 self.lat._GLAT__includeFiles.append(cfgFileName) 01330 return seq 01331 01332 def applyConfig(self, seq=None, gxbrd=None): 01333 """\brief Apply configuration. 01334 01335 \param seq Sequence number of the configuration. 01336 If None, then apply all configurations. 01337 \param gxbrd Apply the GXBRD configuration as well. 01338 Disabled by default. 01339 """ 01340 # RESET state check is disabled until CAL scripts are fixed -ST 01341 ## Check if FSM is None in case we are in a suite 01342 #if self.isRunFromSuite(): 01343 # currentState = self.getSuiteInstance().getScriptState() 01344 #else: 01345 # currentState = self.rc.getFSM().current_state 01346 #if currentState != 'RESET': 01347 # msg = "Can not apply configuration when the run state is %s" % currentState 01348 # log.error(msg) 01349 # raise RuntimeError, msg 01350 #else: 01351 self.lat.applyConfig(seq, gxbrd) 01352 01353 def setArchiverClass(self, archiverClass): 01354 """\brief Set a custom archiver class. 01355 01356 Set a custom archiver class. This is typically done in the __init__ method 01357 of the userApplication class. 01358 01359 \param The archiver class (not the instance) 01360 01361 """ 01362 self.__archiverClass = archiverClass 01363 log.debug("Event archiver set to: %s", self.__archiverClass.__name__) 01364 01365 def getArchiverInstance(self): 01366 """\brief Return the archiver instance. 01367 01368 Returns the archiver instance. Can be used by the user application. 01369 01370 \return Archiver instance 01371 """ 01372 return self.__archiver 01373 01374 def startExcludeTime(self): 01375 """\brief Save the start time for exclusion from the run's elapsed time. 01376 01377 This method allows excluding some amount of time from the run's elapsed 01378 time. The start time is the time that the method is called. 01379 01380 Note that time spent in the PAUSED state is also excluded from the elapsed 01381 time value. Overlapping time is excluded from the elapsed time only once. 01382 """ 01383 if not self.__excluding: 01384 self.__excludeLock.acquire() 01385 now = time.time() 01386 self.__excluding = True 01387 self.__excludeTime = now 01388 self.__elapsedTime += now - self.__strobeTime 01389 self.__strobeTime = now 01390 self.__excludeLock.release() 01391 elif self.__pauseTime is not None: 01392 pass # Going into PAUSED started it 01393 else: 01394 assert not self.__excluding, "Error: " + \ 01395 "Attempt made to exclude time when it is already being excluded" 01396 01397 def endExcludeTime(self): 01398 """\brief Strobe the end time for exclusion from the run's elapsed time. 01399 01400 This method allows excluding some amount of time from the run's elapsed 01401 time. The end time is the time that the method is called. The difference 01402 between the end time and the start time is added to the excluded time. 01403 """ 01404 01405 if self.__pauseTime is None: 01406 assert self.__excluding, "Error: startExcludeTime was not called" 01407 self.__excludeLock.acquire() 01408 now = time.time() 01409 dExcludedTime = now - self.__excludeTime 01410 self.__excludedTime += dExcludedTime 01411 self.__elapsedTime += now - self.__strobeTime - dExcludedTime 01412 self.__strobeTime = now 01413 self.__excludeTime = None 01414 self.__excluding = False 01415 self.__excludeLock.release() 01416 else: # In PAUSED state: nothing to do 01417 pass 01418 01419 def adjustExcludeTime(self, dExcludeTime): 01420 """\brief This method allows adjustment of the amount of time to be 01421 excluded from the run's elapsed time. 01422 01423 The amount of time to be excluded from the run's elapsed time can be 01424 adjusted positivily or negativily with this method. It does not take 01425 into account any other effects on the excluded time, such as whether 01426 the run was in the PAUSED state. 01427 01428 \param dExcludeTime The amount of time to be excluded as given by the 01429 difference of two time.time() values. 01430 """ 01431 # Ensure this doesn't interrupt another thread diddling excludeTime 01432 self.__excludeLock.acquire() 01433 self.__excludedTime += dExcludeTime 01434 self.__excludeLock.require() 01435 01436 def rcSetup(self): 01437 """\brief Setup transition method. 01438 01439 01440 """ 01441 fn = "rcTransitions.rcSetup" 01442 log.debug("%s -- %s" % (fn, self.getName())) 01443 01444 # Set the Global Trigger 01445 if not self.lat.existsGEM(): 01446 glt = self.xbrd.GLT 01447 if glt is None: # temporary: eventually will be explicit in schema 01448 glt = self.xbrd._GXBRD__addGLT() 01449 glt.initialize() 01450 self.setSessionVar('_glt', glt) 01451 01452 # Disable triggers 01453 glt.disableMask() # call this directly. don't use trigger() 01454 01455 # Apply the configuration to the hardware 01456 self.__waitCursor() 01457 self.applyConfig(gxbrd = self.xbrd) 01458 self.__normalCursor() 01459 01460 # set the event mode to take events normally 01461 self.ocs.evtHandlerSendAll() 01462 self.ocs.synchronize() 01463 self.ocs.verifyVersion() 01464 01465 # Disable all the saturation counters, disable the loop and set the delay to 2Hz 01466 for tem in self.lat.TEM.values(): 01467 tic = tem.TIC 01468 tic.satCtrLoopDisable() 01469 tic.satCtrSetLoopDelay(30) 01470 for (regName, regAttr) in tic.regs.items(): 01471 if regName.startswith('sat_'): 01472 tic.satCtrDisable(regAttr.id()) 01473 01474 self.__deadtimeLock.acquire() 01475 self.__deadtimeCtrReg.clear() 01476 self.__deadTimeVal.clear() 01477 self.__deadTimeTS.clear() 01478 self.__avgDeadTime.clear() 01479 self.__deadtimeLock.release() 01480 01481 01482 # The application may pick a different event handler in the SETUP and 01483 # START_RUN transitions. If it doesn't make sure there is a default. 01484 self.selectEventHandler(rcTransitions.EVENT_HANDLER_STANDARD) 01485 01486 # Change as per LTE-193 01487 #hskReadOnly = (self.rc is not None) and (int(self.prefs["envmonreadonly"]) == 1) 01488 hskReadOnly = False 01489 01490 if hskReadOnly: 01491 self.__hsk = rcHouseKeeping.rcHouseKeeping(self.lat, None, rcHouseKeeping.MODE_READONLY) 01492 else: 01493 self.__hsk = rcHouseKeeping.rcHouseKeeping(self.lat, os.path.split(self.__common.getDBN()['_LATSchemaFile'])[1]) 01494 01495 # As per LTE-395, clear the custom rcReport.out dictionary before user setup 01496 self.__customReportData = {} 01497 01498 # Call the application back 01499 try: 01500 status = self.setup() 01501 if status is not None: return status 01502 except: 01503 log.exception("Exception in " + self.getName() + ".setup()") 01504 self.__appStatus = self.COMPL_STATUS_FAILED 01505 return False # Stay in RESET state 01506 01507 # finish setting up the trigger, add the GEM or GLT 01508 if self.lat.existsGEM(): 01509 if self.trigger() is None: 01510 # there's no trigger defined, define a default 01511 # As there's no "real" trigger, don't replace the shut method 01512 self.trigger( rcDefaultTrigger.rcGEMDefaultTrigger(), replaceShut = False ) 01513 self.trigger().GEM(self.lat.downGEM()) 01514 else: 01515 if self.trigger() is None: 01516 # there's no trigger defined, define a default 01517 self.trigger( rcDefaultTrigger.rcMiniGLTDefaultTrigger(), replaceShut = False ) 01518 self.trigger().GLT(glt) 01519 # commit the trigger. This class defined in rcTrg{Gem,MiniGLT}.py 01520 self.trigger().commit() 01521 01522 01523 def rcTeardown(self): 01524 fn = "rcTransitions.rcTeardown" 01525 log.debug("%s -- %s" % (fn, self.getName())) 01526 01527 # Just in case this was missed in __stopRun because of an exception... 01528 self.__strobeTime = None 01529 01530 # Call the application back 01531 try: 01532 status = self.teardown() 01533 except: # Continue as if nothing had happened 01534 log.exception("Exception in " + self.getName() + ".teardown()") 01535 self.__appStatus = self.COMPL_STATUS_FAILED 01536 status = None # Go into RESET state 01537 01538 # reset things defined in rcSetup 01539 self.trigger(None, replaceShut=False) 01540 01541 return status 01542 01543 def rcStartRun(self): 01544 fn = "rcTransitions.rcStartRun" 01545 log.debug("%s -- %s" % (fn, self.getName())) 01546 01547 self.evtCnt = 0 01548 self.__SBCevtCnt = 0 01549 self.__lastEvtCnt = 0 01550 self.__lastSBCevtCnt = 0 01551 self.__dgSize = 0 01552 self.__dgSumSize = 0L 01553 self.__appStatus = None 01554 self.__completionStatus = self.COMPL_STATUS_UNDEFINED 01555 self.__badEvents = 0 01556 self.__errEvents = 0 01557 self.__trgParErrorEvt = 0 01558 self.__pktErrorEvt = 0 01559 self.__endTime = None 01560 self.__startTime = None 01561 self.__excludeLock.acquire() 01562 self.__elapsedTime = 0 01563 self.__strobeTime = None 01564 self.__excluding = False 01565 self.__excludedTime = 0 01566 self.__excludeLock.release() 01567 self.__pauseCount = 0 01568 self.__pausedTime = 0 01569 self.__snapResult = {} 01570 01571 # Initialize the event statistics 01572 self.ocs.stats_failed_events = 0 01573 self.ocs.stats_failed_event_sends = 0 01574 self.ocs.stats_valid_events = 0 01575 self.ocs.stats_handled_events = 0 01576 self.ocs.stats_discarded_event_sends = 0 01577 self.ocs.stats_length_mismatch_count = 0 01578 self.ocs.stats_events_not_sent = 0 01579 self.ocs.stats_filter_pkt_errors = 0 01580 self.ocs.stats_filter_src_errors = 0 01581 self.ocs.stats_filter_passes = 0 01582 self.ocs.stats_filter_fails = 0 01583 01584 self.__common.resetErrorLogCount() 01585 01586 self.__clearEventSizeNTuple() 01587 01588 # This while loop makes sure the session log level filename is different 01589 # than the run level filename by waiting 1 second if necessary. 01590 # This condition may happen in standalone scripts and suites. 01591 while True: 01592 # The timestamp is measured here because things below depend on its value. 01593 # Therefore the run time includes the amount of time spent in the parameter verifier 01594 # in addition to the time the user spends entering the parameters. 01595 self.__tstampRaw = time.gmtime() 01596 self.__tstamp = time.strftime('%y%m%d%H%M%S', self.__tstampRaw) 01597 if self.prefs.has_key("logfilename"): 01598 logfilename = self.prefs["logfilename"] 01599 if self.__tstamp != logfilename[3:15]: 01600 break 01601 else: 01602 break 01603 time.sleep(1) 01604 01605 self.__exportedFiles = [] 01606 01607 # Contains the rcTestReport instance used by the script if any 01608 self.__testReport = None 01609 # Contains the file name for the test report generated by the script 01610 self.__testReportFile = None 01611 01612 self.runId = self.__runIdHelper.nextRunId() 01613 01614 # Check exported directory, as per LTE-226 01615 if int(self.prefs["dataexport"]) == 1: 01616 exportDir = os.path.abspath(self.prefs["exportdir"]) 01617 if not os.path.exists(exportDir): 01618 msg = "Export directory %s does not exist. Aborting run" % exportDir 01619 log.fatal(msg) 01620 return False # reject the transition 01621 01622 # Change as per LTE-193 01623 #hskOutputEnabled = (self.rc is not None) and (int(self.prefs["envmonnbl"]) == 1) 01624 hskOutputEnabled = False 01625 if hskOutputEnabled: 01626 hskFileName = rcHouseKeeping.getFileNameFromTS(self.__tstamp) 01627 self.__hsk.initialize(filePath=self.prefs["logdir"], 01628 fileName=hskFileName, 01629 compress=(int(self.prefs["envmoncompress"]) == 1)) 01630 self.__hsk.start() 01631 01632 # Load the archive file writer, if desired 01633 if self.prefs is not None: 01634 archiveOutputEnabled = (int(self.prefs["datasave"]) == 1) 01635 if archiveOutputEnabled: 01636 archFileName = self.__archiverClass.getFileNameFromTS(self.__tstamp) 01637 self.__archiver = self.__archiverClass(filePath=self.prefs["datadir"], 01638 fileName=archFileName) 01639 self.__archive = self.__archiveIt 01640 else: 01641 self.__archive = self.__archiveDummy 01642 01643 if self.__dds is not None: 01644 self.__publish = self.__publishIt 01645 else: 01646 self.__publish = self.__publishDummy 01647 01648 if self.rc is not None: 01649 self.__paramVerifier.initialize(self.getName()) 01650 self.__verifyStatus = None 01651 01652 # publish the startRun on dds 01653 self.publishConsumerDG('startRun='+self.getRunId()) 01654 01655 # Set the deadtime LRS Counter Mask here to 'total' 01656 # if not specified in a configuration. 01657 for (gtemId, gtem) in self.lat.TEM.items(): 01658 if self.lat.getSysReg(gtem.TIC, 'busy_lrs_mask') is None: 01659 lrsMask = gtem.TIC.busy_lrs_mask 01660 oldLrsMask = lrsMask 01661 if lrsMask & 0x7 != 0x3: 01662 lrsMask |= 0x7 01663 lrsMask &= ~0x4 01664 log.debug("%s: [OVR] TEM[%d].TIC.BUSY_LRS_MASK=0x%08x (0x%08x)" % (fn, gtemId, lrsMask, oldLrsMask)) 01665 gtem.TIC.busy_lrs_mask = lrsMask 01666 01667 # Call the application back 01668 try: 01669 status = self.startRun() 01670 except: 01671 log.exception("Exception in " + self.getName() + ".startRun()") 01672 self.__appStatus = self.COMPL_STATUS_FAILED 01673 status = False # Stay in STOPPED state 01674 if status is not None: 01675 # startRun rejected, cleanup here. 01676 if hskOutputEnabled: 01677 self.__hsk.stop() 01678 return status 01679 01680 if self.__common.options().securedir is not None or self.__common.options().paramverify is not None: 01681 # Don't prompt the parameter verifier if the script 01682 # is in a suite that is called from another suite or 01683 # if it is not the first script in the suite 01684 scriptSeq = 0 01685 if self.isRunFromSuite(): 01686 suiteInst = self.getSuiteInstance() 01687 if suiteInst.getSuite() is None: 01688 scriptSeq = suiteInst.getScriptSequence() 01689 else: 01690 scriptSeq = -1 01691 if scriptSeq == 0 or scriptSeq == 1: 01692 self.__paramVerifier.add("Schema File", os.path.split(self.__common.getDBN()['_LATSchemaFile'])[1]) 01693 self.__paramVerifier.add("Particle Type", self.prefs["lastparticletype"]) 01694 self.__paramVerifier.add("Orientation", self.prefs["lastorientation"]) 01695 self.__paramVerifier.add("Site", self.prefs["lastsite"]) 01696 self.__paramVerifier.add("Instrument Type", self.prefs["lastinstrumenttype"]) 01697 self.__paramVerifier.add("Phase", self.prefs["lastphase"]) 01698 self.__verifyStatus = self.rc.execGUImethod(self.__paramVerifier.verify) 01699 if self.__verifyStatus == 1: # The user successfully signed off on the parameters 01700 pass 01701 elif self.__verifyStatus == 0: # The user cancelled the verification 01702 log.error("Parameter verification failed because the user cancelled verification.") 01703 return self.__verifyStatus 01704 else: 01705 if self.__verifyStatus is None: self.__verifyStatus = -1 01706 log.error("Parameter verification failed due to an unknown reason.") 01707 return self.__verifyStatus 01708 01709 self.__runMsgLogFile = None 01710 if int(self.prefs["lognbl"]) == 1: 01711 self.__runMsgLogFile = os.path.join(self.prefs["logdir"], 01712 "msg" + self.__tstamp + ".log") 01713 self.__runMsgLog = self.__common.startRunLog(self.__runMsgLogFile) 01714 log.info("""%s -- Test %s starting run, Operator: %s,""" 01715 """ Run Id: %s, Time Started: %s -- %s""" 01716 % (fn, self.getName(), self.prefs["operator"], 01717 self.runId, self.__tstamp, time.asctime(self.__tstampRaw))) 01718 01719 # Take snapshot. Note that the triggers must be disabled prior to 01720 # this action, therefore the glt changes are being committed afterwards. 01721 01722 snapshotEnabled = 0 01723 if self.prefs.has_key("snapnbl"): 01724 snapshotEnabled = (int(self.prefs["snapnbl"]) == 1) 01725 if snapshotEnabled: 01726 self.__waitCursor() 01727 01728 snFile = os.path.join(self.prefs["snapshotdir"], 01729 "rsa" + self.__tstamp + ".xml") 01730 self.__snapResult["rsa"] = self.takeSnapshot(stream=None, fileName=snFile, 01731 captureGTIC=1, captureGAEQ=1, trigger=self.trigger()) 01732 self.addExportedFile(snFile,delete=True) 01733 01734 self.__normalCursor() 01735 01736 self.handleEventOutput() 01737 01738 # Clear the event sequence 01739 self.ocs.evtClearEvtSeqMSW() 01740 if self.lat.existsGEM(): 01741 self.lat.GEM.GEMC.sequence = 0 01742 else: 01743 self.trigger().GLT().TAG = 0 01744 self.trigger().GLT().EVENT_NUMBER = 0 01745 01746 # Set up the LCB to deliver events 01747 self.lat.LCB.setEvtEnable(True) 01748 01749 # Purge all event data remaining in socket 01750 purgeSize = self.evtCli.purge() 01751 if purgeSize != 0: 01752 log.warn("Socket purge encountered %d bytes of stale data" % purgeSize) 01753 01754 # Check if the data directories exist and that there's enough space on the disks 01755 if self.rc is not None: 01756 fitsDir = self.prefs["fitsdir"] 01757 archDir = self.prefs["datadir"] 01758 warning = True 01759 for directory in (fitsDir, archDir): 01760 if not os.path.exists(directory): 01761 warning = self.rc.execGUImethod(self.rc.warnDirectoryExist, directory) 01762 elif (FreeSpace.FreeSpace(directory) < rcTransitions.DISKSPACE_WARN_LEVEL): 01763 warning = self.rc.execGUImethod(self.rc.warnFreeSpace, 01764 rcTransitions.DISKSPACE_WARN_LEVEL) 01765 if warning: # OK, continue 01766 pass 01767 else: 01768 return False # reject the transition 01769 01770 # Spawn a thread to handle events 01771 if self.trigger().sweepOnShut(): 01772 self.__eventHandlerReady.clear() # Clear handler ready state 01773 self.rc.eventHandler.spawn(self.__jacket, self.__eventHandlerFn) 01774 self.__eventHandlerReady.wait() # Make sure handler ready 01775 # Sweep out stale events. Status is returned, False = sweep timeout. 01776 swept = self.__sweep() 01777 if not swept: # sweep event has timed out. 01778 log.critical("Sweep event timed out, aborting run and returning to RESET state") 01779 # As per LTE-363 make sure we create a run report and export the run 01780 self.__initStartRun(time.time()) 01781 self.__appStatus = self.COMPL_STATUS_ABORTED 01782 self.__stopRun(fn) 01783 self.rc.doReset() 01784 return False # reject the transition, sweep timeouts are bad 01785 01786 if 'commandSynch' in dir(self): 01787 self.rc.commandSynch.spawn(self.__jacket, self.commandSynch) 01788 else: 01789 if self.trigger().sweepOnShut(): 01790 self.__eventHandlerReady.clear() # Clear handler ready state 01791 evtHandler = Thread(None, self.__eventHandlerFn, 'EventHandler', ()) 01792 evtHandler.start() 01793 self.__eventHandlerReady.wait() # Make sure handler ready 01794 # Sweep out stale events. Status is returned, False = sweep timeout. 01795 swept = self.__sweep() 01796 if not swept: # sweep event has timed out. 01797 log.critical("Sweep event timed out, aborting run and returning to RESET state") 01798 # As per LTE-363 make sure we create a run report and export the run 01799 self.__initStartRun(time.time()) 01800 self.__appStatus = self.COMPL_STATUS_ABORTED 01801 self.__stopRun(fn) 01802 self.rc.doReset() 01803 return False # reject the transition, sweep timeouts are bad 01804 01805 if 'commandSynch' in dir(self): 01806 cmdSynch = Thread(None, self.commandSynch, 'CmdSynch', ()) 01807 cmdSynch.start() 01808 01809 # Handle enabling of the triggers 01810 enableTrigger = True # Default is to enable triggers 01811 if 'triggerEnableOverride' in dir(self): # Allow application to override 01812 enableTrigger = type(self.triggerEnableOverride) == types.MethodType and \ 01813 not self.triggerEnableOverride() 01814 now = time.time() 01815 01816 if enableTrigger: 01817 # Write the user script's trigger mask settings to the hardware 01818 self.trigger().enable() 01819 01820 self.__initStartRun(now) 01821 01822 return status 01823 01824 01825 def __initStartRun(self, now): 01826 #Initialize timers 01827 self.__endTime = None 01828 self.__startTime = now 01829 self.__excludeLock.acquire() 01830 self.__elapsedTime = 0 01831 self.__strobeTime = now 01832 self.__excludeLock.release() 01833 01834 # Set baselines for dead time 01835 self.__deadtimeLock.acquire() 01836 for temId in self.lat.TEM.keys(): 01837 tic = self.lat.TEM[temId].TIC 01838 if tic is not None: 01839 self.__deadtimeCtrReg[temId] = tic.regs['sat_deadtime_lrs_ctr'] 01840 regNo = self.__deadtimeCtrReg[temId].id() 01841 resp = tic.read(regNo) 01842 self.__deadTimeVal[temId] = resp.value() 01843 self.__deadTimeTS[temId] = resp.payloads()[1] 01844 else: 01845 self.__deadTimeVal[temId] = 0 01846 self.__deadTimeTS[temId] = 0 01847 if self.lat.existsGEM(): # __deadTimeVal/TS are dicts, so Jim can add GEM to 'em. :-) 01848 gst = self.lat.GEM.GEMST 01849 value, ts = gst.extCtrRead(gst.regs['livetime'].id()).payloads() 01850 self.__deadTimeVal['GEM'] = value 01851 self.__deadTimeTS['GEM'] = ts 01852 self.__deadTimeVal['LASTGEM'] = value 01853 self.__deadTimeTS['LASTGEM'] = ts 01854 else: 01855 self.__deadTimeVal['GEM'] = 0 01856 self.__deadTimeTS['GEM'] = 0 01857 self.__deadTimeVal['LASTGEM'] = 0 01858 self.__deadTimeTS['LASTGEM'] = 0 01859 self.__deadtimeLock.release() 01860 01861 01862 def __jacket(self, function, *args, **kwargs): 01863 """Jacketting method for catching exceptions and initiating graceful 01864 termination of the user application. 01865 """ 01866 try: 01867 function(*args, **kwargs) 01868 except Exception, e: 01869 import logging 01870 logging.exception("%s: %s" % (function.__name__, e)) 01871 self.__appStatus = self.COMPL_STATUS_ABORTED 01872 self.rc.doReset() 01873 01874 def __handleEventOutput(self): 01875 # Load the FITS file writer, if desired 01876 fn = "__handleEventOutput" 01877 self.__fits = None 01878 if self.prefs is not None: 01879 fitsOutputEnabled = ("fitsnbl" in self.prefs) and (int(self.prefs["fitsnbl"]) == 1) 01880 if fitsOutputEnabled: 01881 try: 01882 fitsFileName = rcFitsWriter.getFileNameFromTS(self.__tstamp) 01883 self.__fits = rcFitsWriter.rcFitsWriter(filePath=self.prefs["fitsdir"], 01884 fileName=fitsFileName, 01885 format=rcFitsWriter.FORMAT_BYTE, 01886 compress=(int(self.prefs["fitscompress"]) == 1), 01887 flushInterval=100, 01888 reportGen=self.__reportGen) 01889 except (ImportError, IOError), e: 01890 log.error("%s: FITS writer disabled: %s" % (fn, e)) 01891 try: 01892 fileName = rcArchiver.rcArchiver.getFileNameFromTS(self.__tstamp) 01893 errFileName = 'err' + fileName.split('.',1)[0] + '.ldf' 01894 self.__errArchiver = rcArchiver.rcArchiver(filePath=self.prefs["datadir"], 01895 fileName=errFileName) 01896 badFileName = 'bad' + fileName.split('.',1)[0] + '.ldf' 01897 self.__badArchiver = rcArchiver.rcArchiver(filePath=self.prefs["datadir"], 01898 fileName=badFileName) 01899 except (ImportError, IOError), e: 01900 log.error("%s: Archiver disabled: %s" % (fn, e)) 01901 01902 def __handleEventOutputStub(self): 01903 pass 01904 01905 def rcStopRun(self): 01906 fn = "rcTransitions.rcStopRun" 01907 log.debug("%s -- %s" % (fn, self.getName())) 01908 01909 # Disable triggers 01910 self.trigger().disable() 01911 01912 # Flush out the remaining triggers in the pipeline and exit evtHandler 01913 swept = self.__flushEvents() 01914 if not swept: # sweep event has timed out. 01915 self.evtCli.abort() # As sweep has timed out, evtHandler hasn't exited. 01916 # force the exit with abort() 01917 01918 self.__common.getDBN()['_event_size'] = 0 01919 01920 # Call the application back after events are no longer flowing 01921 try: 01922 self.stopRun() # RiC: It appears rejecting the transition won't work 01923 except: # Go into STOPPED state 01924 log.exception("Exception in " + self.getName() + ".stopRun()") 01925 self.__appStatus = self.COMPL_STATUS_FAILED 01926 01927 # Stop the run - Must be done after the application stopRun() 01928 self.__stopRun(fn) 01929 01930 def rcStop(self): 01931 fn = "rcTransitions.rcStop" 01932 log.debug("%s -- %s" % (fn, self.getName())) 01933 01934 # Disable triggers 01935 self.trigger().disable() 01936 01937 # Flush out the remaining triggers in the pipeline and exit evtHandler 01938 swept = self.__flushEvents() 01939 if not swept: # sweep event has timed out. 01940 self.evtCli.abort() # As sweep has timed out, evtHandler hasn't exited. 01941 # force the exit with abort() 01942 01943 self.__common.getDBN()['_event_size'] = 0 01944 01945 # Mark the resume time 01946 now = time.time() 01947 self.__pausedTime += (now - self.__pauseTime) 01948 self.__pauseTime = None 01949 01950 if not self.__excluding: 01951 self.__excludeLock.acquire() 01952 dExcludedTime = now - self.__excludeTime 01953 self.__excludedTime += dExcludedTime 01954 self.__elapsedTime += now - self.__strobeTime - dExcludedTime 01955 self.__strobeTime = now 01956 self.__excludeTime = None 01957 self.__excludeLock.release() 01958 else: # Time is still being excluded: nothing to do 01959 pass 01960 01961 # Call the application back after events are no longer flowing 01962 try: 01963 self.stop() # RiC: It appears rejecting the transition won't work 01964 except: # Go into STOPPED state 01965 log.exception("Exception in " + self.getName() + ".stop()") 01966 self.__appStatus = self.COMPL_STATUS_FAILED 01967 01968 # Stop the run 01969 self.__stopRun(fn) 01970 01971 def rcPause(self): 01972 fn = "rcTransitions.rcPause" 01973 log.debug("%s -- %s" % (fn, self.getName())) 01974 01975 # Sweep out any currently buffered triggers 01976 if self.trigger().sweepOnShut(): 01977 swept = self.__sweep() 01978 if not swept: 01979 log.critical("Sweep event timed out, aborting run and returning to RESET state") 01980 self.rc.doReset() 01981 return False # reject the transition, sweep timeouts are bad 01982 01983 # Increment pause count 01984 self.__pauseCount += 1 01985 01986 # Calculate elapsed time so far 01987 now = time.time() 01988 self.__pauseTime = now 01989 01990 if not self.__excluding: 01991 self.__excludeLock.acquire() 01992 self.__excludeTime = now 01993 self.__elapsedTime += now - self.__strobeTime 01994 self.__strobeTime = now 01995 self.__excludeLock.release() 01996 else: # Time is already being excluded: nothing to do 01997 pass 01998 01999 # Call the application back 02000 try: 02001 status = self.pause() 02002 except: # Go into PAUSED state 02003 log.exception("Exception in " + self.getName() + ".pause()") 02004 self.__appStatus = self.COMPL_STATUS_FAILED 02005 02006 # Do a pseudo-resume if the PAUSE transition is rejected 02007 if status is not None: 02008 # Mark the resume time before reenabling triggers 02009 now = time.time() 02010 self.__pausedTime += (now - self.__pauseTime) 02011 self.__pauseTime = None 02012 02013 if not self.__excluding: 02014 self.__excludeLock.acquire() 02015 dExcludedTime = now - self.__excludeTime 02016 self.__excludedTime += dExcludedTime 02017 self.__elapsedTime += now - self.__strobeTime - dExcludedTime 02018 self.__strobeTime = now 02019 self.__excludeTime = None 02020 self.__excludeLock.release() 02021 else: # Time is still being excluded: nothing to do 02022 pass 02023 02024 # Reenable triggers 02025 self.trigger().enable() 02026 02027 return status 02028 02029 def rcResume(self): 02030 fn = "rcTransitions.rcResume" 02031 log.debug("%s -- %s" % (fn, self.getName())) 02032 02033 # Mark the resume time before reenabling triggers 02034 now = time.time() 02035 02036 # Call the application back 02037 try: 02038 self.trigger().enable() 02039 status = self.resume() 02040 except: # Go into RUNNING state 02041 self.trigger().disable() 02042 log.exception("Exception in " + self.getName() + ".resume()") 02043 self.__appStatus = self.COMPL_STATUS_FAILED 02044 02045 # Don't change the situation if the application rejected the transition 02046 if status is None: 02047 self.__pausedTime += (now - self.__pauseTime) 02048 self.__pauseTime = None 02049 02050 if not self.__excluding: 02051 self.__excludeLock.acquire() 02052 dExcludedTime = now - self.__excludeTime 02053 self.__excludedTime += dExcludedTime 02054 self.__elapsedTime += now - self.__strobeTime - dExcludedTime 02055 self.__strobeTime = now 02056 self.__excludeTime = None 02057 self.__excludeLock.release() 02058 else: # Time is still being excluded: nothing to do 02059 pass 02060 02061 # Reenable triggers 02062 self.trigger().enable() 02063 02064 return status 02065 02066 # Not used anymore - ST 02067 #def extend(self): 02068 # """Overload this function to provide additional space after the event in 02069 # the datagram. This space can be used to add application dependent 02070 # LATcontributions to the datagram. This method is evaluated at startRun time 02071 # and not on a per trigger basis. It should return a Python string big enough 02072 # to fit whatever additional space is needed for the user data. To gain 02073 # access to this space use the LATdatagram.alloc(size) method. 02074 # """ 02075 # return "" 02076 02077 # The following methods are meant to be overridden by the application 02078 02079 def getName(self): 02080 return self.__class__.__name__ 02081 02082 def setup(self): 02083 "Method that is called on the SETUP transition" 02084 log.debug("rcTransitions.setup()") 02085 # The transition can be rejected by not returning None 02086 return None 02087 02088 def teardown(self): 02089 "Method that is called on the TEARDOWN transition" 02090 log.debug("rcTransitions.teardown()") 02091 # The transition can be rejected by not returning None 02092 return None 02093 02094 def startRun(self): 02095 "Method that is called on the START_RUN transition" 02096 log.debug("rcTransitions.startRun()") 02097 # The transition can be rejected by not returning None 02098 return None 02099 02100 def stopRun(self): 02101 "Method that is called on the STOP_RUN transition" 02102 log.debug("rcTransitions.stopRun()") 02103 # The transition can be rejected by not returning None 02104 return None 02105 02106 def pause(self): 02107 "Method that is called on the PAUSE transition" 02108 log.debug("rcTransitions.pause()") 02109 # The transition can be rejected by not returning None 02110 return None 02111 02112 def resume(self): 02113 "Method that is called on the RESUME transition" 02114 log.debug("rcTransitions.resume()") 02115 # The transition can be rejected by not returning None 02116 return None 02117 02118 def stop(self): 02119 "Method that is called on the STOP transition" 02120 log.debug("rcTransitions.stop()") 02121 # The transition can be rejected by not returning None 02122 return None 02123 02124 def process(self, eventBuffer): 02125 "Method that is called on every data event taken" 02126 log.debug("rcTransitions.process()") 02127 02128 02129 # Private methods appear below 02130 02131 ## private: 02132 02133 def __clearEventSizeNTuple(self): 02134 # Clear the event size plot ntuple 02135 dbn = self.__common.getDBN() 02136 if '_EventSizeNTuple' in dbn: 02137 dbn['_EventSizeNTuple'].clear() 02138 02139 def __normalCursor(self): 02140 if self.rc is not None and 'normalCursor' in dir(self.rc): 02141 self.rc.normalCursor() 02142 02143 def __waitCursor(self): 02144 if self.rc is not None and 'waitCursor' in dir(self.rc): 02145 self.rc.waitCursor() 02146 02147 def __eventProgress(self): 02148 """ 02149 Method used by the sweep logic to time out waiting for sweep events. 02150 This function returns the number of events that were handled by the 02151 event handler since the last time it was called. 02152 """ 02153 progress = self.evtCnt - self.__lastEvtCnt 02154 self.__lastEvtCnt = self.evtCnt 02155 self.__lastSBCevtCnt = self.__SBCevtCnt 02156 return progress 02157 02158 def __sweep(self): 02159 """ 02160 Method used to ensure that the event pipeline is swept of pending events. 02161 When this method returns triggers are disabled but set up as they were upon 02162 entry. 02163 """ 02164 02165 # Wait for marked event to show up while pipelined events drain 02166 self.__waitCursor() 02167 # Make sure TEM registers are in a 'good' state for event data taking 02168 # and solicit the sweep trigger. 02169 swept = self.trigger().shut(self.__sweepEvent, self.__eventProgress) 02170 self.__normalCursor() 02171 # Triggers are disabled at this point. 02172 02173 return swept 02174 02175 def __flushEvents(self): 02176 # Cause eventHandler to exit when the sweep marker is seen 02177 02178 # Handle the race condition when event handler 02179 # may exit before the sweep event is received. 02180 self.__sweepEvent.clear() 02181 self.__evtHandlerQuit = True 02182 02183 swept = True 02184 # Sweep any buffered triggers 02185 if self.trigger().sweepOnShut(): 02186 swept = self.__sweep() 02187 02188 # Handle the race condition when event handler 02189 # may exit before the sweep event is received. 02190 self.__sweepEvent.set() 02191 return swept 02192 02193 02194 def __stopRun(self, fn='__stopRun'): 02195 # Calculate the final elapsed time 02196 now = time.time() 02197 self.__endTime = now 02198 self.__excludeLock.acquire() 02199 if self.__strobeTime is not None: 02200 self.__elapsedTime += now - self.__strobeTime 02201 self.__strobeTime = None 02202 self.__excludeLock.release() 02203 02204 self.__avgDeadTime = self.__calcDeadTime() 02205 02206 # Change as per LTE-193 02207 #hskOutputEnabled = (self.rc is not None) and (int(self.prefs["envmonnbl"]) == 1) 02208 hskOutputEnabled = False 02209 if hskOutputEnabled: 02210 self.__hsk.stop() 02211 02212 # publish the endRun on dds 02213 self.publishConsumerDG('endRun='+self.getRunId()) 02214 02215 if self.__completionStatus == self.COMPL_STATUS_UNDEFINED: 02216 log.warn("Script completed with an undefined completion status. " 02217 "Please use the setCompletionStatus method.") 02218 02219 # Close fits file 02220 if self.__fits is not None: 02221 self.__fits.close(self.rc.inShutdown) 02222 if self.__badArchiver is not None: 02223 self.__badArchiver.close() 02224 self.__removeZeroLengthFile(self.__badArchiver.getFilePath(), self.__badArchiver.getFileName()) 02225 # need to check if file was removed before adding to export list.. 02226 _fnameTmp = os.path.join(self.__badArchiver.getFilePath(), self.__badArchiver.getFileName()) 02227 if os.path.exists(_fnameTmp): 02228 self.addExportedFile(_fnameTmp, delete=True) 02229 self.__badArchiver = None 02230 if self.__errArchiver is not None: 02231 self.__errArchiver.close() 02232 self.__removeZeroLengthFile(self.__errArchiver.getFilePath(), self.__errArchiver.getFileName()) 02233 # need to check if file was removed before adding to export list.. 02234 _fnameTmp = os.path.join(self.__errArchiver.getFilePath(), self.__errArchiver.getFileName()) 02235 if os.path.exists(_fnameTmp): 02236 self.addExportedFile(_fnameTmp, delete=True) 02237 self.__errArchiver = None 02238 02239 if self.__archiver is not None: 02240 self.__archiver.close() 02241 02242 # Optional call back to perform processing once the archive file is closed 02243 # and before it gets exported. 02244 self.processArchive(os.path.join(self.__archiver.getFilePath(), self.__archiver.getFileName())) 02245 02246 if self.prefs is not None: 02247 inShutDown = False 02248 if self.rc is not None and self.rc.inShutdown: 02249 inShutDown = True 02250 if not inShutDown: 02251 snapshotEnabled = 0 02252 if self.prefs.has_key("snapnbl"): 02253 snapshotEnabled = (int(self.prefs["snapnbl"]) == 1) 02254 if snapshotEnabled: 02255 self.__waitCursor() 02256 02257 snFile = os.path.join(self.prefs["snapshotdir"], 02258 "rsb" + self.__tstamp + ".xml") 02259 self.__snapResult["rsb"] = self.takeSnapshot(stream=None, fileName=snFile, 02260 captureGTIC=1, captureGAEQ=1, 02261 trigger=self.trigger()) 02262 self.addExportedFile(snFile, delete=True) 02263 02264 self.__normalCursor() 02265 02266 # Let the operator override the completion status and add comments. 02267 if self.__common.options().securedir is not None or self.__common.options().paramverify is not None: 02268 if self.rc is not None and not self.rc.inShutdown: 02269 if not self.isRunFromSuite() or self.isLastSuiteScript(): 02270 self.rc.execGUImethod(self.rc.handleEndRunDlg) 02271 02272 # Create test report 02273 if self.rc is not None: 02274 if not self.rc.inShutdown: 02275 self.rc.execGUImethod(self.__genReport) 02276 else: 02277 self.__genReport() 02278 02279 self.__logEndRun(fn) 02280 curDir = os.getcwd() 02281 self.__exportData() 02282 if os.path.exists(curDir): 02283 os.chdir(curDir) 02284 02285 def __logEndRun(self, fn): 02286 log.info("%s -- Test %s finished, Run Id: %s, Status: %s, Completion Time: %s" 02287 % (fn, self.getName(), self.runId, self.getCompletionStatusStr(), 02288 time.asctime(time.gmtime(self.__endTime)))) 02289 02290 if int(self.prefs["lognbl"]) == 1: 02291 self.__common.stopRunLog(self.__runMsgLog) 02292 self.addExportedFile(self.__runMsgLogFile, delete=True) 02293 02294 def processArchive(self, archivePath): 02295 """\brief Optional call back to perform processing on the archive file. 02296 02297 This method will be called after the archive file is closed and before it is exported. 02298 User applications can override this method to perform custom processing on the file. 02299 02300 \param archivePath Full path of the archive file. 02301 02302 """ 02303 pass 02304 02305 def __calcDeadTime(self, temId=None): 02306 avgDeadTimes = {} 02307 if temId is None: 02308 tems = self.lat.TEM.keys() 02309 else: 02310 tems = [temId] 02311 for tId in tems: 02312 if tId in self.__deadtimeCtrReg and self.__startTime is not None: 02313 # Disable command debugging for status panel methods 02314 # which access hardware 02315 saveDebug = self.__cmdCli.getDebug() 02316 self.__cmdCli.setDebug(0) 02317 regNo = self.__deadtimeCtrReg[tId].id() 02318 try: 02319 resp = self.__deadtimeCtrReg[tId].getNode().read(regNo) 02320 except gException.LATInterfaceException, e: 02321 if e.errstr()[0] == 'OCS_TMO': return {} 02322 else: raise 02323 newVal = resp.value() 02324 newTS = resp.payloads()[1] 02325 self.__cmdCli.setDebug(saveDebug) 02326 02327 dTime = newVal - self.__deadTimeVal[tId] 02328 # Compensate for the non-running time. 02329 excludeTime = (time.time() - self.__startTime - self.__getElapsedTime()) * rcTransitions.TIMEBASE_RATE_IN_MHZ * 1000000 02330 dTS = newTS - self.__deadTimeTS[tId] - excludeTime 02331 if dTime < 0: 02332 dTime += (1L << 32) 02333 if dTS < 0: 02334 dTS += (1L << 64) 02335 if dTS != 0: 02336 avgDeadTime = (float(dTime)/float(dTS)) 02337 avgDeadTime *= (rcTransitions.TIMEBASE_RATE_IN_MHZ / rcTransitions.DEADTIME_RATE_IN_MHZ) * 100.0 02338 else: 02339 avgDeadTime = None 02340 avgDeadTimes[tId] = avgDeadTime 02341 else: 02342 avgDeadTimes[tId] = None 02343 return avgDeadTimes 02344 02345 def __removeZeroLengthFile(self, filePath, fileName): 02346 fname = os.path.join(filePath, fileName) 02347 size = os.stat(fname)[6] 02348 if size == 0: 02349 try: 02350 os.remove(fname) 02351 except: 02352 return -1 02353 return 0 02354 02355 def __exportData(self): 02356 """ 02357 Routine to export all relevant data from a run to 02358 local storage and possibly to offsite backup. 02359 This routine is designed to execute after the completion of a run, 02360 but before teardown of Run Control. 02361 The name of the export directory is the run ID. This is 02362 to guarantee against unintentional overwrites between multiple 02363 teststands. 02364 """ 02365 02366 # If we aren't exporting, return 02367 exportEnabled = (int(self.prefs["dataexport"]) == 1) 02368 if not exportEnabled: return 02369 02370 # first thing is to create a local directory structure 02371 outDir = self.getRunId() 02372 outDirRoot = self.prefs["datadir"] 02373 outPath = os.path.join(outDirRoot,outDir) 02374 02375 # Define two exporters. 02376 # One which purges the files after export, one which doesn't. 02377 hardExporter = DataExport.DataExport() 02378 safeExporter = DataExport.DataExport() 02379 hardExporter.setPurge(True) 02380 safeExporter.setPurge(False) 02381 02382 # append to the list of exported files 02383 02384 # schema configuration: Don't delete these! 02385 self.addExportedFile(self.getSchemaConfigFile(), delete=False) 02386 if self.lat is not None: # additional files from GLAT... 02387 for f in self.lat._GLAT__includeFiles: 02388 self.addExportedFile(f, delete=False) 02389 02390 if os.access(outDirRoot,os.W_OK): # can we write? 02391 os.mkdir(outPath) 02392 02393 # list of files that get hardExported. 02394 dataSave = (int(self.prefs["datasave"]) == 1) 02395 if dataSave: 02396 moveList = [ self.__archiver, self.__fits ] 02397 for fileObj in moveList: 02398 if fileObj is not None: 02399 self.addExportedFile(os.path.join(fileObj.getFilePath(), fileObj.getFileName()), delete=True) 02400 02401 # export housekeeping file 02402 # Change as per LTE-193 02403 #hskReadOnly = (int(self.prefs["envmonreadonly"]) == 1) 02404 #if self.__hsk is not None and (int(self.prefs["envmonnbl"]) == 1) and not hskReadOnly: 02405 # self.addExportedFile(self.__hsk.getFileName(full=1), delete=True) 02406 02407 # make the rcReport.out file 02408 rcLastLine = self.__rcLastLine() # Grab the info from rcReport.out 02409 rcOutFile = open(os.path.join(outPath,"rcReport.out"), 'w') 02410 rcOutFile.write(rcLastLine) 02411 rcOutFile.close() 02412 02413 for path,name,delete in self.__exportedFiles: 02414 try: 02415 if delete: 02416 hardExporter.export(os.path.join(path,name),os.path.join(outPath,name)) 02417 else: 02418 safeExporter.export(os.path.join(path,name),os.path.join(outPath,name)) 02419 except Exception, e: 02420 log.error("Exception %s in trying to export file %s" % (e, name)) 02421 02422 else: # Can't write to outDirRoot 02423 err = "rcTransitions.__exportData: Local directory ", outDirRoot, " is not writable" 02424 log.error(err) 02425 return # Can't continue exporting, so return 02426 02427 02428 # if the data dir and the export dir are the same, don't export. 02429 if os.path.abspath(self.prefs["datadir"]) == os.path.abspath(self.prefs["exportdir"]): 02430 msg = "Data source and Export directories are identical\n" + \ 02431 "source: %s, export: %s" % ( self.prefs["datadir"] , self.prefs["exportdir"] ) 02432 log.warn(msg) 02433 exportEnabled = False 02434 02435 # Export the directory to permanent storage 02436 if exportEnabled: 02437 if not os.path.exists(os.path.abspath(self.prefs["exportdir"])): 02438 log.fatal("Final export directory %s no longer exists. Something has gone very, very wrong." % self.prefs["exportdir"]) 02439 return 02440 02441 destDir = os.path.join(self.prefs["exportdir"] ,outDir) 02442 dbn = self.__common.getDBN() 02443 dbn['_LastExportPath'] = destDir 02444 sourceDir = outPath 02445 hardExporter.export(sourceDir, destDir) 02446 02447 # done export. Create an "I'm done" file in the export area 02448 done = file(destDir+'/zzz.done','w') 02449 done.close() 02450 02451 def __rcLastLine(self): 02452 """Get the last line of the run report file. 02453 """ 02454 # Now, we need the info from rcReport.out 02455 # As this routine runs after the writing of the file, just grab 02456 # the last usable line. 02457 filename = ( os.path.join(self.prefs["reportdir"], 'rcReport.out') ) 02458 02459 rcFile = open(filename,'r') 02460 charsPerLine = 4096 # this probably needs tuning. 02461 while 1: 02462 try: # read from the end of the file 02463 rcFile.seek(-1 * charsPerLine ,2) 02464 except IOError: # the file's shorter than charsPerLine 02465 rcFile.seek(0) # reposition at head 02466 if rcFile.tell() == 0: 02467 atstart=True 02468 else: 02469 atstart=False 02470 02471 rcLines=rcFile.read().split("\n") # last line of file is blank, 02472 if (len(rcLines) > 2) or atstart: # so require 2 full lines 02473 break 02474 # The lines are bigger than we thought, 02475 charsPerLine=charsPerLine * 2 # so double the read 02476 02477 rcFile.close() 02478 rcLastLine = rcLines[len(rcLines)-2] # cache this for later 02479 02480 # extract the run id from rcLastLine, and compare it with what's expected 02481 import re 02482 runId = 0 02483 runIdRE = re.compile(r".*<RunId>(\d+)</RunId>.*") 02484 rList = runIdRE.findall(rcLastLine) 02485 if len(rList) > 0: 02486 runId = rList[0] 02487 else: 02488 log.fatal("Could not parse a run ID out of the last line of rcReport.out.") 02489 log.error("Last line = %s" % rcLastLine) 02490 if runId != self.getRunId(): 02491 msg = "Run identifier read from rcReport.out (%s) does not match LATTE internal run ID (%s)" % (runId, self.getRunId()) 02492 log.fatal(msg) 02493 02494 02495 return rcLastLine 02496 02497 def selectEventHandler(self, type, customEventHandler = None): 02498 """Use this method to select an event handler to use. 02499 The application can call this method from the SETUP or START_RUN 02500 transitions. If it doesn't, a default is used. 02501 """ 02502 currentState = self.getState() 02503 if currentState is None: 02504 # Standalone mode - disable state check 02505 currentState = 'RESET' 02506 if currentState == 'RESET' or currentState == 'STOPPED': 02507 if type == rcTransitions.EVENT_HANDLER_STANDARD: 02508 self.__eventHandlerFn = self.__eventHandler 02509 elif type == rcTransitions.EVENT_HANDLER_LEAN_AND_MEAN: 02510 self.__eventHandlerFn = self.__datagramHandler 02511 elif type == rcTransitions.EVENT_HANDLER_PLAYBACK: 02512 self.__eventHandlerFn = self.__eventHandlerPlayback 02513 elif type == rcTransitions.EVENT_HANDLER_CUSTOM: 02514 self.__eventHandlerFn = customEventHandler 02515 else: 02516 log.error("Unrecognized eventHandler type") 02517 else: 02518 log.error("The event handler can not be changed in the %s state" % \ 02519 (currentState)) 02520 02521 # Private method for calling the application back with event data 02522 def __eventHandler(self): 02523 """Handler for calling the application back with event data 02524 """ 02525 evtCli = self.evtCli 02526 endRunMarker = rcTransitions.__SWEEP_MARKER 02527 02528 # On playback, see if we need to trigger an event 02529 if self.__EBFplayback: 02530 # If application DIDN'T enable the internal trigger, 02531 # enable it and trigger first event 02532 if not self.trigger().conditions().solicited(): 02533 playbackTrigger = True 02534 # two cases, GEM/GLT. 02535 # force an enable of the solicit in both cases. 02536 if self.lat.existsGEM(): 02537 from LATTE.trigger.TrgGem import TrgGem 02538 self.trigger().enable(TrgGem._TrgGem__SOLICIT_MASK) 02539 else: 02540 from LATTE.trigger.TrgMiniGLT import TrgMiniGLT 02541 self.trigger().enable(TrgMiniGLT._TrgMiniGLT__SOLICIT_MASK) 02542 02543 self.trigger().solicit() 02544 else: # Application is taking care of triggering the event 02545 playbackTrigger = False 02546 02547 self.__evtHandlerQuit = False 02548 02549 # Indicate that the Event Handler is ready to take events 02550 self.__eventHandlerReady.set() 02551 02552 while True: 02553 try: 02554 # Go wait for an event to show up 02555 status = evtCli.readEvent() 02556 if status == EvtCli.ABORT_ERROR: 02557 break 02558 02559 # Count every event to ensure displayed rate makes sense 02560 self.evtCnt += 1 02561 self.__dgSize = evtCli.header[2] 02562 self.__dgSumSize += evtCli.header[2] 02563 02564 # ST - We want to pass the event as is so that we can check what type 02565 # of error it has in user script's process() method 02566 #parser_status = evtCli.readEvent() 02567 #if evtCli.checkOnlineStatus(parser_status, EvtCli.GGLT_STATUS_ERROR): 02568 # status = evtCli.getGGLTStatus() 02569 #else: 02570 # status = parser_status 02571 02572 if (status == 0) or evtCli.checkOnlineStatus(status, EvtCli.PLAYBACK_ERROR): 02573 self.errorEvent = evtCli.isErrorEvent() 02574 self.trgParErrorEvent = evtCli.isTrgParityErrorEvent() 02575 self.packetErrorEvent = evtCli.isPacketErrorEvent() 02576 summaryMarker = evtCli.evt.summary.marker 02577 self.eventMarker = summaryMarker 02578 if summaryMarker == endRunMarker: 02579 self.__sweepEvent.set() # Signal that the marked event was seen 02580 # Commented out, since we want the sweep marker event to be seen by process() 02581 #if self.__evtHandlerQuit: break 02582 #continue 02583 02584 # For efficiency first check whether event playback is enabled only once 02585 if self.__EBFplayback: 02586 # Whenever evtSvr detects errors during playback, stop the current run 02587 if evtCli.checkOnlineStatus(status, EvtCli.PLAYBACK_ERROR): 02588 self.rc.doStop() 02589 continue 02590 02591 # Since we know we're in playback mode, trigger the next event if 02592 # the application DIDN'T enable the internal trigger 02593 if playbackTrigger: 02594 self.trigger().solicit() 02595 else: # Application is taking care of triggering the event 02596 pass 02597 02598 # Allow application to process all kinds of events 02599 #try: 02600 #self.process((status,evtCli.dat)) 02601 latContribs = self.__process(status, evtCli.dat) 02602 #except: 02603 # log.exception("") 02604 # self.__appStatus = self.COMPL_STATUS_FAILED 02605 # self.rc.doReset() 02606 02607 # Policy: don't write out unnavigable or empty events 02608 if status != 0: 02609 self.__badEvents+=1 02610 if evtCli.dat is not None: 02611 try: 02612 self.__badArchiver.write(evtCli.dat) 02613 except AttributeError: 02614 # Archiver may be disabled by the --noerrfile option 02615 pass 02616 else: 02617 (l,) = struct.unpack('!L', evtCli.dat[4:8]) 02618 if self.__fits is not None: 02619 self.__fits.write(evtCli.dat[:l]) 02620 if self.errorEvent or self.packetErrorEvent or self.trgParErrorEvent: 02621 if summaryMarker != endRunMarker: 02622 try: 02623 self.__errArchiver.write(evtCli.dat[:l]) 02624 except AttributeError: 02625 # Archiver may be disabled by the --noerrfile option 02626 pass 02627 # increment internal error counters 02628 if self.errorEvent: self.__errEvents += 1 02629 if self.trgParErrorEvent: self.__trgParErrorEvt += 1 02630 if self.packetErrorEvent: self.__pktErrorEvt += 1 02631 02632 self.__archive(evtCli.header[1], latContribs) 02633 #if self.__dds is not None: 02634 self.__publish(evtCli.dat) 02635 except IOError: 02636 # Handle the event timeout if enabled 02637 if str(sys.exc_info()[1]) == "Event read timeout": 02638 evtCli.dat = '' 02639 status = EvtCli.READ_TIMEOUT_ERROR 02640 else: 02641 self.trigger().disable() 02642 raise IOError, "Unhandled IOError in rcTransitions::readEvent: %s" % sys.exc_info()[1] 02643 02644 if self.__evtHandlerQuit and self.__sweepEvent.isSet(): break 02645 02646 # HEADS UP: At this point evtCli.dat could be byteswapped 02647 02648 log.debug('rcTransitions.__eventHandler: %s terminating' % (self.getName())) 02649 02650 02651 def __archiveIt(self, datagramId, latContribs): 02652 # Calculate the length of the entire datagram 02653 size = 8 # Datagram ID and its length 02654 for latContrib in latContribs: 02655 size += len(latContrib) 02656 02657 # Create the datagram header 02658 dgHdr = struct.pack('>2L', datagramId, size) 02659 02660 # Archive the datagram and all its contributions 02661 self.__archiver.writeTuple((dgHdr,) + latContribs) 02662 02663 def __archiveDummy(self, datagramId, latContribs): 02664 pass 02665 02666 def __archiveBad(self, datagram): 02667 archiver = self.__badArchiver 02668 if archiver is not None: 02669 archiver.write(datagram) 02670 02671 def __archiveErr(self, datagram): 02672 archiver = self.__errArchiver 02673 if archiver is not None: 02674 archiver.write(datagram) 02675 02676 def __publishIt(self, datagram): 02677 self.__dds.publish(datagram) 02678 02679 def __publishDummy(self, datagram): 02680 pass 02681 02682 def __process(self, status, datagram): 02683 try: 02684 # Make a copy of the whole event (not just a reference) 02685 # that proccess can byte swap. - Revisit for efficiency 02686 #dgBuf = str(buffer(datagram)) 02687 #import array 02688 #dgBuf = array.array('L', datagram).tostring() 02689 dgBuf = datagram[:1] + datagram[1:] 02690 02691 latContribs = self.process((status, dgBuf)) 02692 if latContribs is not None: return latContribs 02693 except Exception, e: 02694 import logging 02695 logging.exception("Trapped userApplication:process() exception: %s" % e) 02696 self.__appStatus = self.COMPL_STATUS_FAILED 02697 self.rc.doReset() 02698 02699 # RiC: The following may be a better solution to the copy done above 02700 # if datagram[0:4] == LDF.LATdatagram.ID: datagram.byteswap() 02701 02702 return (datagram[8:],) # LATcontributions, not LATdatagrams 02703 02704 def __datagramHandler(self): 02705 """Handler for calling the application back with event datagrams 02706 """ 02707 02708 sweepEvt = self.__sweepEvent 02709 evtCli = self.evtCli 02710 sweepMkr = rcTransitions.__SWEEP_MARKER 02711 markerMsk = 0x7 << 22 02712 errorMsk = 0x1 << 21 02713 trgParMsk = 0x1 02714 pktErrMsk = 0x7 << 13 # 02715 02716 self.__evtHandlerQuit = False 02717 02718 # Indicate that the Event Handler is ready to take events 02719 self.__eventHandlerReady.set() 02720 02721 while not (self.__evtHandlerQuit and sweepEvt.isSet()): 02722 02723 # Get the data from the socket 02724 header, datagram = evtCli.readSocket() 02725 02726 # Count every event to ensure displayed rate makes sense 02727 self.evtCnt += 1 02728 self.__dgSize = header[2] 02729 self.__dgSumSize += header[2] 02730 02731 # Give application quick access to error bit and marker 02732 self.errorEvent = (header[6] & errorMsk) != 0 02733 self.trgParErrorEvent = (header[6] & trgParMsk ) 02734 self.packetErrorEvent = ( (header[5] & pktErrMsk) != 0 ) 02735 self.eventMarker = (header[6] & markerMsk) >> 22 02736 02737 02738 # Process the datagram 02739 latContribs = self.__process(header[0], datagram) 02740 02741 # Check FSW event delivery to OES status 02742 if header[0] == 0: 02743 # Archive the data 02744 self.__archive(header[1], latContribs) 02745 02746 # Multicast the data 02747 self.__publish(datagram) 02748 02749 # Handle datagrams with TEM contribution errors 02750 if self.eventMarker != sweepMkr and \ 02751 (self.errorEvent or self.trgParErrorEvent or self.packetErrorEvent): 02752 self.__archiveErr(datagram) 02753 # increment internal error counters 02754 if self.errorEvent: self.__errEvents += 1 02755 if self.trgParErrorEvent: self.__trgParErrorEvt += 1 02756 if self.packetErrorEvent: self.__pktErrorEvt += 1 02757 else: 02758 # Handle datagrams for which FSW choked 02759 self.__badEvents += 1 02760 self.__archiveBad(datagram) 02761 02762 # Make sure the above is done before setting the sweepEvt to prevent 02763 # things from getting out of synch. 02764 if self.eventMarker == sweepMkr: sweepEvt.set() 02765 02766 log.debug('rcTransitions.__datagramHandler: %s terminating' % 02767 (self.getName())) 02768 02769 02770 def __eventHandlerPlayback(self): 02771 """Handler for calling the application back with played back event data 02772 """ 02773 pass # Currently handled by __eventHandler() above 02774 02775 def publishConsumerDG(self, buf): 02776 import LDF 02777 # Pad the contributions with zeros to force it to be an integer number of 02778 # longwords in length 02779 remainder = len(buf) & 0x3 02780 if remainder: buf += "\x00\x00\x00"[0:4-remainder] 02781 # need to byteswap the raw buffer if little endian 02782 if sys.byteorder == "little": 02783 import array 02784 buf = array.array('L',buf) 02785 buf.byteswap() 02786 buf = buf.tostring() 02787 # get type for this UDF contribution 02788 # pending definition of this contribution, use Scratch 02789 primary = LDF.LATprimaryId().Base_Scratch 02790 secondary = LDF.LATsecondaryId().Base_Scratch 02791 typeId = LDF.LATtypeId(primary, secondary) 02792 # construct header 02793 dg = LDF.LATdatagram() 02794 # 8's are to accomodate pairs of long words of the UDF heade 02795 # opaque is length of my contribution with header in bytes 02796 dg = struct.pack('>4L',dg.ID,len(buf)+len(dg)+8,typeId.value(),len(buf)+8) 02797 dg = dg+str(buf) 02798 self.__publish(dg) 02799 time.sleep(0.05) 02800 02801 class ApplicationCallbacks(object): 02802 def beforeSetup(self, app): 02803 pass 02804 def afterSetup(self, app): 02805 pass 02806 def beforeStartRun(self, app): 02807 pass 02808 def afterStartRun(self, app): 02809 pass 02810 def beforeStopRun(self, app): 02811 pass 02812 def afterStopRun(self, app): 02813 pass 02814 def beforeTeardown(self, app): 02815 pass 02816 def afterTeardown(self, app): 02817 pass 02818 02819 class StandaloneLauncher(object): 02820 def __init__(self, logLevel="INFO"): 02821 self.__common = RunControlCommon() 02822 self.__common.initialize() 02823 self.__common.setLoggingLevel(logLevel) 02824 self.__common.connect() 02825 02826 def launch(self, appClass, user, callbacks=None): 02827 if appClass is None: 02828 module = self.__common.options().app 02829 try: 02830 (module, filename) = rcUtil.importModule(module, reload=0) 02831 except Exception, e: 02832 log.exception(e) 02833 return 02834 ua = module.userApplication(None, user, self.__common) 02835 if callbacks is None and 'userApplicationCallbacks' in dir(module): 02836 callbacks = module.userApplicationCallbacks() 02837 else: 02838 ua = appClass(None, user, self.__common) 02839 02840 if callbacks is not None: 02841 callbacks.beforeSetup(ua) 02842 ua.rcSetup() 02843 if callbacks is not None: 02844 callbacks.afterSetup(ua) 02845 if callbacks is not None: 02846 callbacks.beforeStartRun(ua) 02847 ua.rcStartRun() 02848 if callbacks is not None: 02849 callbacks.afterStartRun(ua) 02850 ua.wait() 02851 if callbacks is not None: 02852 callbacks.beforeStopRun(ua) 02853 ua.rcStopRun() 02854 if callbacks is not None: 02855 callbacks.afterStopRun(ua) 02856 if callbacks is not None: 02857 callbacks.beforeTeardown(ua) 02858 ua.rcTeardown() 02859 if callbacks is not None: 02860 callbacks.afterTeardown(ua)