00001 #!/usr/local/bin/python 00002 # 00003 # Copyright 2002 00004 # by 00005 # The Board of Trustees of the 00006 # Leland Stanford Junior University. 00007 # All rights reserved. 00008 # 00009 00010 __facility__ = "Online" 00011 __abstract__ = "Housekeeping FITS file generation class for RunControl" 00012 __author__ = "S. Tuvi <stuvi@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online" 00013 __date__ = ("$Date: 2005/03/03 19:52:37 $").split(' ')[1] 00014 __version__ = "$Revision: 2.4 $" 00015 __release__ = "$Name: R04-12-00 $" 00016 __credits__ = "SLAC" 00017 00018 import LATTE.copyright_SLAC 00019 00020 import os 00021 from cfitsio import * 00022 import time 00023 from struct import unpack 00024 import threading 00025 import operator 00026 import calendar 00027 import logging as log 00028 00029 MODE_CREATE = 0 00030 MODE_READONLY = 2 00031 00032 TEM_ENV_REGS = 0 00033 AEM_ENV_REGS = 1 00034 00035 HSK_VERSION_STR = '1.0' 00036 00037 TEM_ENV_REG_LIST = [ 00038 'tkr_biasdac', 00039 'cal_biasdac', 00040 'adc_tkr_digital_2_5v', 00041 'adc_tkr_digital_2_5i', 00042 'adc_tkr_analog_a_1_5v', 00043 'adc_tkr_analog_a_1_5i', 00044 'adc_tkr_analog_b_2_5v', 00045 'adc_tkr_analog_b_2_5i', 00046 'adc_tkr_bias_v', 00047 'adc_tkr_bias_i', 00048 'adc_cal_digital_3_3v', 00049 'adc_cal_digital_3_3i', 00050 'adc_cal_analog_3_3v', 00051 'adc_cal_analog_3_3i', 00052 'adc_cal_bias_v', 00053 'adc_cal_bias_i', 00054 'adc_tem_digital_3_3v', 00055 'adc_tem_digital_3_3i', 00056 'adc_afee0_t0', 00057 'adc_afee0_t1', 00058 'adc_afee1_t0', 00059 'adc_afee1_t1', 00060 'adc_afee2_t0', 00061 'adc_afee2_t1', 00062 'adc_afee3_t0', 00063 'adc_afee3_t1', 00064 'adc_tkr_c0_t0', 00065 'adc_tkr_c0_t1', 00066 'adc_tkr_c1_t0', 00067 'adc_tkr_c1_t1', 00068 'adc_tkr_c2_t0', 00069 'adc_tkr_c2_t1', 00070 'adc_tkr_c3_t0', 00071 'adc_tkr_c3_t1', 00072 'adc_tkr_c4_t0', 00073 'adc_tkr_c4_t1', 00074 'adc_tkr_c5_t0', 00075 'adc_tkr_c5_t1', 00076 'adc_tkr_c6_t0', 00077 'adc_tkr_c6_t1', 00078 'adc_tkr_c7_t0', 00079 'adc_tkr_c7_t1', 00080 'sat_cal_lrs_ctr_0', 00081 'sat_cal_lrs_ctr_1', 00082 'sat_tkr_lrs_ctr_0', 00083 'sat_tkr_lrs_ctr_1', 00084 'sat_tkr_lrs_ctr_2', 00085 'sat_tkr_lrs_ctr_3', 00086 'sat_deadtime_lrs_ctr' 00087 ] 00088 00089 AEM_ENV_REG_LIST = [ 00090 'env_free_00', 00091 'env_free_01', 00092 'env_free_02', 00093 'env_free_03', 00094 'env_free_04', 00095 'env_free_05', 00096 'env_free_06', 00097 'env_free_07', 00098 'env_free_08', 00099 'env_free_09', 00100 'env_free_10', 00101 'env_free_11', 00102 ] 00103 00104 class rcHouseKeeping(object): 00105 def __init__(self, lat, latFilename, mode=MODE_CREATE, tableSize=1000, flushInterval=0): 00106 self.__lat = lat 00107 self.__latFilename = latFilename 00108 self.__threadQuit = 0 00109 self.__threadPause = 0 00110 self.__envRegs = [] 00111 self.__envUnits = [] 00112 self.__envNames = [] 00113 self.__updateInterval = 1.0 # default to 1 second 00114 self.__sampleCount = 0 00115 self.__envValues = [] 00116 self.__envTStamps = [] 00117 self.__tableSize = tableSize 00118 self.__fptr = None 00119 self.__mode = mode 00120 self.__flushInterval = flushInterval 00121 self.__status = 0 00122 self.__fileName = None 00123 self.__filePath = None 00124 00125 def initialize(self, filePath=None, fileName=None, compress=0): 00126 if self.__mode == MODE_CREATE: 00127 if filePath is not None or fileName is not None: 00128 if fileName is None: 00129 ts = time.strftime('%y%m%d%H%M%S', time.gmtime()) 00130 self.__fileName = getFileNameFromTS(ts) 00131 self.__timestamp = int(time.time()) 00132 else: 00133 self.__fileName = fileName 00134 try: 00135 self.__timestamp = int(calendar.timegm( 00136 time.strptime(self.__fileName.split('.',1)[0][3:], '%y%m%d%H%M%S') 00137 )) 00138 except: 00139 self.__timestamp = 0 00140 if compress: self.__fileName += '.gz' 00141 if filePath is None: 00142 self.__filePath = "" 00143 else: 00144 self.__filePath = filePath 00145 fname = os.path.join(self.__filePath, self.__fileName) 00146 (self.__status, self.__fptr) = cfitsio.fits_create_file(fname) 00147 if self.__status != 0: 00148 raise IOError, "fits_create_file: %s filename=%s" % (cfitsio.fits_get_errstatus(self.__status), fname) 00149 naxis = 0 00150 naxes = [] 00151 self.__status = cfitsio.fits_create_img(self.__fptr, cfitsio.SHORT_IMG, naxis, naxes) 00152 if self.__status != 0: 00153 raise IOError, "fits_create_img: %s" % cfitsio.fits_get_errstatus(self.__status) 00154 comment1 = "Housekeeping data file" 00155 status = cfitsio.fits_write_comment(self.__fptr, " ") 00156 status = cfitsio.fits_write_comment(self.__fptr, comment1) 00157 self.__status = cfitsio.fits_write_key_str(self.__fptr, "HSK_VER", HSK_VERSION_STR, "Housekeeping File Version") 00158 self.checkStatus("fits_write_key_str", self.__status) 00159 self.__status = cfitsio.fits_write_key_str(self.__fptr, "SCHEMA", self.__latFilename, "Schema File Name") 00160 self.checkStatus("fits_write_key_str", self.__status) 00161 self.__status = cfitsio.fits_write_key_lng(self.__fptr, "TIMESTMP", 00162 self.__timestamp, "Housekeeping Timestamp") 00163 self.__tablenum = 0 00164 elif self.__mode == MODE_READONLY: 00165 pass 00166 else: 00167 raise RuntimeError, "Invalid Housekeeping mode" 00168 00169 def createNewTable(self): 00170 tfields = len(self.__envNames) + 1 00171 ttype = ['timestamp'] + self.__envNames 00172 tform = ['1D'] + ['1V'] * (tfields - 1) 00173 tunit = ['seconds'] + self.__envUnits 00174 extname='Environmental Quantities' 00175 self.__status = cfitsio.fits_create_tbl(self.__fptr, cfitsio.BINARY_TBL, 00176 self.__tableSize, tfields, ttype, tform, tunit, extname) 00177 if self.__status != 0: 00178 raise IOError, "fits_create_tbl: %s" % cfitsio.fits_get_errstatus(self.__status) 00179 self.__status = cfitsio.fits_write_key_lng(self.__fptr, "RECCOUNT", 00180 0, "Record Count") 00181 if self.__status != 0: 00182 raise IOError, "fits_write_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00183 #for i in range(tfields): 00184 # hdrLabel = "TDISP%d" % (i+1) 00185 # if i == 0: 00186 # hdrValue = "F10.3" 00187 # else: 00188 # hdrValue = "I12.1" 00189 00190 hdrLabel = "TDISP1" 00191 hdrValue = "F10.3" 00192 00193 self.__status = cfitsio.fits_write_key_str(self.__fptr, hdrLabel, hdrValue, "") 00194 self.checkStatus("fits_write_key_str", self.__status) 00195 self.__rownum = 1 00196 self.__tablenum += 1 00197 00198 def write(self): 00199 if self.__tablenum == 0: 00200 self.createNewTable() 00201 elif self.__rownum > self.__tableSize: 00202 self.__status = cfitsio.fits_update_key_lng(self.__fptr, "RECCOUNT", 00203 self.__rownum - 1, "Record Count") 00204 if self.__status != 0: 00205 raise IOError, "fits_update_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00206 if self.__flushInterval > 0: 00207 self.__status = cfitsio.fits_flush_file(self.__fptr) 00208 if self.__status != 0: 00209 raise IOError, "fits_flush_file: %s" % cfitsio.fits_get_errstatus(self.__status) 00210 self.createNewTable() 00211 00212 00213 colNum = 1 00214 recTstamp = max(self.__envTStamps) 00215 self.__status = cfitsio.fits_write_col_dbl(self.__fptr, colNum, self.__rownum, 00216 1, 1, [recTstamp]) 00217 if self.__status != 0: 00218 raise IOError, "fits_write_col_dbl: %s" % cfitsio.fits_get_errstatus(self.__status) 00219 for value in self.__envValues: 00220 colNum += 1 00221 self.__status = cfitsio.fits_write_col_lng(self.__fptr, colNum, self.__rownum, 00222 1, [value]) 00223 if self.__status != 0: 00224 raise IOError, "fits_write_col_lng %s" % cfitsio.fits_get_errstatus(self.__status) 00225 00226 if (self.__flushInterval > 0) and (self.__rownum % self.__flushInterval == 0): 00227 self.__status = cfitsio.fits_update_key_lng(self.__fptr, "RECCOUNT", 00228 self.__rownum, "Record Count") 00229 if self.__status != 0: 00230 raise IOError, "fits_update_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00231 self.__status = cfitsio.fits_flush_buffer(self.__fptr, 0) 00232 if self.__status != 0: 00233 raise IOError, "fits_flush_buffer: %s" % cfitsio.fits_get_errstatus(self.__status) 00234 self.__rownum +=1 00235 00236 00237 def moveToHDU(self, tablenum): 00238 (self.__status, hdutype) = cfitsio.fits_movabs_hdu(self.__fptr, tablenum) 00239 if self.__status != 0: 00240 return self.__status 00241 (self.__status, self.__eventCount, comment) = cfitsio.fits_read_key_lng(self.__fptr, 'RECCOUNT') 00242 if self.__status != 0: 00243 raise IOError, "fits_read_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00244 return self.__status 00245 00246 00247 def read(self): 00248 self.__rownum += 1 00249 if self.__rownum > self.__eventCount: 00250 self.__tablenum += 1 00251 status = self.moveToHDU(self.__tablenum) 00252 if status != 0: 00253 return None 00254 self.__rownum = 1 00255 (self.__status, event_size, heap_addr) = cfitsio.fits_read_descript(self.__fptr, 1, self.__rownum) 00256 if self.__status != 0: 00257 raise IOError, "fits_read_descript: %s" % cfitsio.fits_get_errstatus(self.__status) 00258 (self.__status, dat) = cfitsio.fits_read_col_byt(self.__fptr, 1, self.__rownum, 1, event_size, None) 00259 if self.__status != 0: 00260 raise IOError, "fits_read_col_byt: %s" % cfitsio.fits_get_errstatus(self.__status) 00261 return dat 00262 00263 00264 def close(self): 00265 if self.__fptr is not None: 00266 if self.__mode != MODE_READONLY: 00267 if self.__tablenum > 0: 00268 self.__status = cfitsio.fits_update_key_lng(self.__fptr, "RECCOUNT", 00269 self.__rownum - 1, "Event Count") 00270 if self.__status != 0: 00271 raise IOError, "fits_update_key_lng: %s" % cfitsio.fits_get_errstatus(self.__status) 00272 self.__status = cfitsio.fits_close_file(self.__fptr) 00273 if self.__status != 0: 00274 raise IOError, "fits_close_file: %s" % cfitsio.fits_get_errstatus(self.__status) 00275 00276 def setFlushInterval(self, flushInterval): 00277 self.__flushInterval = flushInterval 00278 00279 def getStatus(self): 00280 return self.__status 00281 00282 def getFileName(self, full=0): 00283 if self.__filePath is None or self.__fileName is None: 00284 return None 00285 elif full: 00286 return os.path.join(self.__filePath, self.__fileName) 00287 else: 00288 return self.__fileName 00289 00290 def setEnvRegs(self, regList, componentId=None): 00291 self.__envRegs = [] 00292 self.__envUnits = [] 00293 self.__envNames = [] 00294 self.addEnvRegs(regList, componentId) 00295 00296 def addEnvRegs(self, regList, componentId=None): 00297 if operator.isNumberType(regList): 00298 if regList == TEM_ENV_REGS: 00299 if componentId is None: 00300 raise RuntimeError, "Please specify from which TEM " \ 00301 "the environment values are going to be collected." 00302 if not self.__lat.existsTEM(componentId): 00303 raise RuntimeError, "TEM[%d] node not found in LAT hierarchy" % componentId 00304 tem = self.__lat.TEM[componentId] 00305 if not tem.existsTIC(): 00306 raise RuntimeError, "GTIC node not found in LAT hierarchy" 00307 regNode = tem.TIC 00308 regNames = TEM_ENV_REG_LIST 00309 elif regList == AEM_ENV_REGS: 00310 if not self.__lat.existsAEM(): 00311 raise RuntimeError, "AEM node not found in LAT hierarchy" 00312 aem = self.__lat.AEM 00313 if not aem.existsAEQ(): 00314 raise RuntimeError, "GAEQ node not found in LAT hierarchy" 00315 regNode = aem.AEQ 00316 regNames = AEM_ENV_REG_LIST 00317 else: 00318 raise RuntimeError, "Invalid register template id: %d" % regList 00319 regList = [] 00320 for regName in regNames: 00321 reg = regNode.regs[regName] 00322 regList.append(regNode.regs[regName]) 00323 self.__envRegs += regList 00324 for reg in self.__envRegs: 00325 node = reg.getNode() 00326 ids = str(node.getIdTuple()) 00327 if ids[-2] == ',': 00328 ids = ids[:-2] + ids[-1] 00329 self.__envNames.append(self.getRegLabel(reg)) 00330 self.__envUnits.append(reg.getEGU().units()) 00331 00332 def setUpdateInterval(self, updateInterval): 00333 self.__updateInterval = updateInterval 00334 00335 def stop(self): 00336 self.__threadQuit = 1 00337 if self.__envThread.isAlive(): 00338 self.__envThread.join() 00339 self.close() 00340 00341 def pause(self): 00342 self.__threadPause = 1 00343 00344 def resume(self): 00345 self.__threadPause = 0 00346 00347 def getRegLabel(self, reg): 00348 node = reg.getNode() 00349 ids = str(node.getIdTuple()) 00350 if ids[-2] == ',': 00351 ids = ids[:-2] + ids[-1] 00352 return node.getName() + ids + "." + reg.getName() 00353 00354 00355 00356 def getSampleCount(self): 00357 return self.__sampleCount 00358 00359 def getEnvNames(self): 00360 return self.__envNames 00361 00362 def getCachedValues(self): 00363 return self.__envValues 00364 00365 def getCachedValue(self, reg, bypass=1, strVal=0): 00366 regName = reg.getName() 00367 regLabel = self.getRegLabel(reg) 00368 if regLabel in self.__envNames: 00369 val = self.__envValues[self.__envNames.index(regLabel)] 00370 if bypass == 0: 00371 if self.__mode != MODE_READONLY: 00372 val = reg.getEGU().egu(val) 00373 if strVal == 1: 00374 val = '%8.2f' %val + "%-2s" %reg.getEGU().units() 00375 else: 00376 if self.__mode == MODE_READONLY: 00377 val = reg.getEGU().raw(val) 00378 if strVal == 1: 00379 val = str(val) 00380 return val 00381 else: 00382 #print regName, "not found in", self.__envNames 00383 return None 00384 00385 def getEnvQuantities(self, regList, bypass=0): 00386 values = [] 00387 tstamps = [] 00388 for reg in regList: 00389 if reg.isCommand() or reg.isWriteOnly() or reg.isNoDirectAccess(): 00390 raise RuntimeError, "Invalid environmental quantity register: %s" % reg.getName() 00391 try: 00392 value = reg.get(bypass) 00393 except: 00394 value = 0 00395 tstamp = reg.getTimeStamp() 00396 name = reg.getName() 00397 values.append(value) 00398 tstamps.append(tstamp) 00399 return (values, tstamps) 00400 00401 def updateValues(self): #revisit 00402 if self.__mode == MODE_READONLY: 00403 (values, tstamps) = self.getEnvQuantities(self.__envRegs, bypass=0) 00404 self.__envValues = values 00405 else: 00406 (values, tstamps) = self.getEnvQuantities(self.__envRegs, bypass=1) 00407 self.__envValues = map(long, values) 00408 self.__envTStamps = tstamps 00409 if self.__fptr is not None: 00410 self.write() 00411 self.__sampleCount += 1 00412 00413 def start(self): 00414 self.__threadQuit = 0 00415 self.__envThread = threading.Thread(target=self.run,name="HouseKeeping") 00416 self.__envThread.start() 00417 # Let the thread get at least one measurement 00418 time.sleep(1) 00419 00420 def run(self): 00421 if len(self.__envRegs) == 0: 00422 if self.__lat.existsTEM(0): 00423 self.addEnvRegs(TEM_ENV_REGS, 0) 00424 if self.__lat.existsAEM(): 00425 self.addEnvRegs(AEM_ENV_REGS) 00426 if not self.__lat.existsTEM(0) and not self.__lat.existsAEM(): 00427 #Probably in GASU mode, exit the thread 00428 return 00429 while(not self.__threadQuit): 00430 # get data here 00431 if not self.__threadPause: 00432 self.updateValues() 00433 t0 = time.time() 00434 while (time.time() - t0 < self.__updateInterval): 00435 time.sleep(.001) 00436 if self.__threadQuit: 00437 break 00438 00439 def checkStatus(self, cfitsio_function, status): 00440 if status != 0: 00441 raise IOError, "%s: %s" % (cfitsio_function, cfitsio.fits_get_errstatus(status)) 00442 00443 # ---- End rcHouseKeeping ----- 00444 00445 def getFileNameFromTS(ts): 00446 return "env" + ts + ".fits" 00447