Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

rcTransitions.py

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)

Generated on Fri Jul 21 13:26:31 2006 for LATTE R04-12-00 by doxygen 1.4.3