FILE.py

Go to the documentation of this file.
00001 #!/usr/local/bin/python
00002 #
00003 #                               Copyright 2005
00004 #                                     by
00005 #                        The Board of Trustees of the
00006 #                     Leland Stanford Junior University.
00007 #                            All rights reserved.
00008 #
00009 #
00010 # References:  [FSW_GUIDE]    LAT-TD-07665-01    LAT FSW User Guide
00011 #
00012 #
00013 
00014 
00015 
00016 __facility__ = "Online"
00017 __abstract__ = "FILE related mnemonics"
00018 __author__   = "Selim Tuvi <stuvi@slac.stanford.edu> SLAC - GLAST LAT I&T/Online"
00019 __date__     = "2005/07/23 00:08:27"
00020 __updated__  = "$Date: 2006/04/28 01:29:35 $"
00021 __version__  = "$Revision: 1.18 $"
00022 __release__  = "$Name: HEAD $"
00023 __credits__  = "SLAC"
00024 
00025 import LICOS.copyright_SLAC
00026 
00027 import array
00028 import logging as log
00029 import os
00030 import tempfile
00031 import time
00032 
00033 from cmdTlmDb import CmdPkg
00034 
00035 from LICOS.lib.LATconstants            import *
00036 from LICOS.lib.cmdTlmDb.TelemetryEvent import TelemetryEvent
00037 from LICOS.lib.LATfunctions            import utcfromtimestamp
00038 
00039 # Define our tempfile paths for consistency
00040 if os.name == 'posix':
00041   FILETOOL_TMP_PATH = "/tmp"
00042 else:   # gotta be windows...
00043   FILETOOL_TMP_PATH = os.path.normpath(os.path.join('C:/', 'TEMP'))
00044 
00045 # executables to add FMX headers and compression to binary uploads
00046 if os.name == 'posix':
00047   FILETOOL_HDR_ALLOW = True
00048   FSW_ADD_HEADER_CMD = 'file_hdr_prefix'     # flight/SVC/symlnk/linux-gcc/exe/zcompress
00049   FSW_COMPRESS_CMD   = 'zcompress'           # flight/SVC/symlnk/linux-gcc/exe/file_hdr_prefix
00050 else:
00051   FILETOOL_HDR_ALLOW = False                 # do NOT allow addition of fsw headers from Windows
00052   FSW_ADD_HEADER_CMD = None
00053   FSW_COMPRESS_CMD   = None
00054 
00055 # hook for keeping track of an arbitrary file number (last part of a FswFileID)
00056 FILETOOL_ARBITRARY_FILENUMBER  = 0
00057 # hook for keeping track of an arbitrary FMX key  (not to be used with real FMX)
00058 FILETOOL_ARBITRARY_FMXID       = 0x20000000L
00059 
00060 # packet, mnemonic and 'source' for primary boot FILESTAT state
00061 FILE_PBC_FILESTAT = { FSW_UNIT_SIU : [ "LBTHKP",      "LPBCFILESTAT",  "" ],
00062                       FSW_UNIT_EPU0: [ "LBTEPU0HKP",  "LPBC0FILESTAT", "diag" ],
00063                       FSW_UNIT_EPU1: [ "LBTEPU1HKP",  "LPBC1FILESTAT", "diag" ],
00064                       FSW_UNIT_EPU2: [ "LBTEPU2HKP",  "LPBC2FILESTAT", "diag" ],
00065                       }
00066 
00067 
00068 class FILE(CmdPkg):
00069   """!\brief FILE telecommand package class
00070 
00071   Provides access to FILE telecommands.
00072   """
00073   def __init__(self, db):
00074     """!\brief FILE constructor.
00075 
00076     \param db  Wrapper database object for the generated module
00077               containing command and telemetry database classes.
00078     """
00079     CmdPkg.__init__(self, db)
00080     self.__primaryBoot=False
00081 
00082 
00083 
00084   def downloadFile(self, unit, fileID):
00085     """!\brief Download a file from the LAT
00086 
00087     \param unit        LAT unit id to upload to
00088     \param fileID      An FswFileID object
00089     \return An FswFile object containing the data uploaded
00090     """
00091     class FileDump(object):
00092       def __init__(self, master):
00093         self.__master = master
00094         self.__cmdDb = master.getCmdDb()
00095         self.__fragments = {}
00096         self.__dumpSizes = {}
00097         self.__expectedID = None
00098         self.__dumpCTDBtelem = TelemetryEvent(self.__cmdDb.getVsc().getDiagHandler(),
00099                                               [self.__cmdDb.getVsc().getTlmDb().getApidFromName('LLFSDUMPCTDB')],
00100                                               self.dumpCTDB)
00101       def dumpCTDB(self, telemetry):
00102         # File chunks may be sent down out of order.
00103         # Cache each dump payload in a dictionary keyed on the file offset.
00104         try:
00105           pkt = self.__cmdDb.getVsc().getTlmDb().decodeTelemetry(telemetry)
00106           offset   = pkt.get_payload("LFSFDMPOFFSET")
00107           dumpSize = pkt.get_payload("LFSFDMPSIZE")
00108 
00109           # Check if this is part of the expected file
00110           dumpDev  = pkt.get_payload("LFSFDMPDEV")
00111           dumpDir  = pkt.get_payload("LFSFDMPDIR")
00112           dumpFile = pkt.get_payload("LFSFDMPFILE")
00113 
00114           if dumpDev  != self.__expectedID.device() or \
00115                  dumpDir  != self.__expectedID.directory() or \
00116                  dumpFile != self.__expectedID.fileNumber():
00117             msg = "CTDB dump resulted in unexpected file: %s, expected %s" % \
00118                   ( str(FswFileID(dumpDev, dumpDir, dumpFile)), self.__expectedID )
00119             raise RuntimeError(msg)
00120 
00121           dataMnems = ['LFSFDMPDATA%d' % i for i in xrange(390)]
00122           self.__fragments[offset] = ''.join(map(chr, map(pkt.get_payload, dataMnems)))
00123           self.__dumpSizes[offset] = dumpSize
00124 
00125         except Exception, e:
00126           print e
00127           raise
00128 
00129       def dump(self, unit, fileID):
00130         self.__expectedID = fileID
00131         self.__dumpCTDBtelem.enable()
00132         LFS = self.__cmdDb.LFS
00133 
00134         # need to know how much we expect
00135         expSize = None
00136         dir = self.__cmdDb.FILE.dumpDirectory(unit,fileID)
00137         files = dir.files()
00138         for f in files:
00139           if f.id.id() == fileID.id():
00140             expSize = f.size
00141 
00142         LFS.LFSFILEDUMPC( LFSFILEID=fileID.id(),
00143                           LFSUNIT=unit,
00144                           LFSPAD16=0 )
00145 
00146         # wait until we get the expected size
00147         timeWait = 0
00148         oldSize = 0
00149         downSize = sum(self.__dumpSizes.values())
00150         while downSize < expSize and timeWait < SHORT_TIMEOUT*5:
00151           # reset if we're still getting data
00152           if downSize > oldSize:
00153             timeWait = 0
00154           time.sleep(0.1)
00155           timeWait += 0.1
00156           downSize = sum(self.__dumpSizes.values())
00157 
00158         # reassemble
00159         offsets = self.__fragments.keys()
00160         offsets.sort()
00161         binary = ""
00162         for o in offsets:
00163           binary += self.__fragments[o][:self.__dumpSizes[o]]
00164 
00165         status = self.__master.checkLastCommandStatus()
00166         self.__dumpCTDBtelem.disable()
00167 
00168         return FswFileFromData(fileID, binary, uploaded=True)
00169 
00170     fd = FileDump(self.getCmdDb())
00171 
00172     fswFile = fd.dump(self.__LFSUNIT(unit), fileID)
00173 
00174     return fswFile
00175 
00176 
00177   def downloadFileToSSR(self, unit, fileID):
00178     """!\brief Download a file from the LAT
00179 
00180     \param app         A testAppBase object  (your user application...)
00181     \param unit        LAT unit id to upload to
00182     \param fileID      An FswFileID object
00183     \return status
00184     """
00185     self.getCmdDb().LFS.LFSFILEDUMPS( LFSFILEID=fileID.id(),
00186                                       LFSunit=self.__LFSUNIT(unit),
00187                                       LFSPAD16=0 )
00188     status = self.checkLastCommandStatus()
00189     return status
00190 
00191   def dumpDirectory(self, unit, directoryID, root=False):
00192     """!\brief Download a directory listing from the LAT
00193 
00194     \param unit        LAT unit id to upload to
00195     \param fileID      An FswFileID object with the correct directory set.
00196     \return An FswFile object containing the data uploaded
00197     """
00198     class DirDump(object):
00199       def __init__(self, master, root):
00200         self.__master = master
00201         self.__cmdDb  = master.getCmdDb()
00202         self.__root   = root
00203         self.__rep    = None
00204         self.__lastTime = time.time()
00205         self.__dumpSizes = {}
00206         if self.__root:
00207           wantedPacket = "LLFSROOTLIST"
00208         else:
00209           wantedPacket = "LLFSDIRLIST"
00210 
00211 
00212         self.__dumpDirTelem = TelemetryEvent(self.__cmdDb.getVsc().getDiagHandler(),
00213                                              [self.__cmdDb.getVsc().getTlmDb().getApidFromName(wantedPacket)],
00214                                              self.getPackets)
00215       def getPackets(self, telemetry):
00216         self.__lastTime = time.time()
00217         try:
00218           pkt = self.__cmdDb.getVsc().getTlmDb().decodeTelemetry(telemetry)
00219           if self.__root:
00220             prefix = 'LFSRDMPF'
00221           else:
00222             prefix = 'LFSDDMP'
00223 
00224           device    = pkt.get_payload( prefix + "DEV" )
00225           directory = pkt.get_payload( prefix + "DIR" )
00226           fileNum   = pkt.get_payload( prefix + "FILE" )
00227           # Deal with LCAT inconsistencies
00228           if self.__root:
00229             prefix = prefix[:-1]
00230 
00231           ro        = pkt.get_payload( prefix + "ROFLG")
00232           size      = pkt.get_payload( prefix + "SIZE")
00233           isdir     = pkt.get_payload( prefix + "DIRFLG")
00234           t         = pkt.get_payload( prefix + "TIME")
00235           u         = pkt.get_payload( prefix + "UNIT")
00236           blocks    = 0  # pkt.get_payload( prefix + "BLOCKS")  # bad?  why?
00237 
00238 
00239           id = FswFileID(device, directory, fileNum)
00240           item = FswDirectoryItem(id,
00241                                   ro, size, isdir, t, u, blocks)
00242           
00243           self.__rep.addItem(item)
00244           #print "getPackets:", str(id), str(self.__rep.id())
00245 
00246           # for real files in LLFSDIRLIST, we get the header!
00247           if not self.__root:
00248             dataMnems = [prefix+ 'FHDR%d' %i for i in xrange(32)]
00249             hdr = ''.join(map(chr, map(pkt.get_payload, dataMnems)))
00250             item.header = hdr
00251           
00252         except Exception, e:
00253           print e
00254           raise
00255 
00256       def dump(self, unit, dirID):
00257         self.__rep = FswDirectory( dirID=dirID )
00258         self.__dumpDirTelem.enable()
00259         self.__cmdDb.LFS.LFSDIRDUMP(LFSFILEID=dirID.id(),
00260                                     LFSUNIT=unit,
00261                                     LFSPAD16=0)
00262         # REVISIT:  need to wait for the entire directory to come down
00263         while True:
00264           if time.time() - self.__lastTime > 2.0:
00265             break
00266           else:
00267             time.sleep(0.1)
00268         status = self.__master.checkLastCommandStatus()
00269         
00270         time.sleep(1)
00271 
00272         self.__dumpDirTelem.disable()
00273 
00274         return self.__rep
00275 
00276     dd = DirDump(self.getCmdDb(), root)
00277     dirRep = dd.dump(self.__LFSUNIT(unit), directoryID)
00278     return dirRep
00279 
00280   def createDirectory(self, unit, directoryID):
00281     """!\brief create a directory on a LAT file system
00282 
00283     \param unit        LAT unit id
00284     \param directoryID An FswFileID object with the correct directory set.
00285     \return status     True if it worked, False if not.
00286     """
00287     if directoryID.directory() not in FSW_DIR_RANGE:
00288       log.error("%s attempted to create directory %d which is out of the allowed range" %\
00289                 ("FILE.createDirectory", directoryID.directory()) )
00290       return False
00291     
00292     self.getCmdDb().LFS.LFSDIRCREATE(LFSUNIT=self.__LFSUNIT(unit),
00293                                      LFSFILEID=directoryID.id(),
00294                                      LFSPAD16=0)
00295     status = self.checkLastCommandStatus()
00296     return status
00297 
00298   def deleteDirectory(self, unit, directoryID, force=False):
00299     """!\brief Delete a directory on a LAT filesystem
00300 
00301     \param unit         LAT unit id to upload to
00302     \param directoryID  An FswFileID object with the correct directory set.
00303     \return status      True if success, False if failure
00304     """
00305     if directoryID.directory() not in FSW_DIR_RANGE:
00306       log.error("%s attempted to delete directory %d which is out of the allowed range" %\
00307                 ("FILE.deleteDirectory", directoryID.directory()) )
00308       return False
00309     
00310     status = True
00311     if force:
00312       # Remove all files in this directory
00313       dirRep = self.dumpDirectory(unit, directoryID)
00314       for f in dirRep.files():
00315         status = self.deleteFile(unit, f.id)
00316         if not status:
00317           break
00318     if status:
00319       # log.debug("deleting directory %s" % directoryID)
00320       self.getCmdDb().LFS.LFSDIRDELETE(LFSUNIT=self.__LFSUNIT(unit),
00321                                        LFSFILEID=directoryID.id(),
00322                                        LFSPAD16=0)
00323       status = self.checkLastCommandStatus()
00324 
00325     return status
00326 
00327 
00328   def uploadFileFromDisk(self, fileName, unit, fileID):
00329     """!\brief Upload a disk resident file with an FMX header
00330 
00331     \param app         A testAppBase object  (your user application...)
00332     \param fileName    Fully qualified file name
00333     \param unit        LAT unit id to upload to
00334     \param fileID      An FswFileID object
00335     \return An FswFile object containing the data uploaded
00336     """
00337     uplFile = None
00338     try:
00339       uplFile = FswFileFromDisk(fileID, fileName)
00340     except:
00341       raise
00342     else:
00343       self.uploadFile(uplFile, unit)
00344     return uplFile
00345 
00346   def uploadFileFromData(self, fileData, unit, fileID):
00347     """!\brief Upload a memory resident file with an FMX header (Typically received from MOOT)
00348 
00349     \param app         A testAppBase object  (your user application...)
00350     \param fileData    Binary representation of an FMX file
00351     \param unit        LAT unit id to upload to
00352     \param fileID      An FswFileID object
00353     \return An FswFile object containing the data uploaded
00354     """
00355     uplFile = None
00356     try:
00357       uplFile = FswFileFromData(fileID, fileData)
00358     except:
00359       raise
00360     else:
00361       self.uploadFile(uplFile, unit)
00362     return uplFile
00363 
00364   def cancelUpload(self, unit):
00365     """!\brief cancel any in process uploads
00366 
00367     \param app         A testAppBase object  (your user application...)
00368     \param fswFile     FswFile object
00369     \param unit        LAT unit id to upload to
00370     \return status
00371     """
00372     self.LFILUPLCANCEL(LFILEUNIT=self.__LFSUNIT(unit))
00373     status = self.checkLastCommandStatus()
00374     return status
00375 
00376   def uploadFile(self, fswFile, unit):
00377     """!\brief Upload a memory resident fsw file object
00378 
00379     \param app         A testAppBase object  (your user application...)
00380     \param fswFile     FswFile object
00381     \param unit        LAT unit id to upload to
00382     \return status
00383     """
00384 
00385     # TODO:  JHP-  Check for uploaded?
00386     # if fswFile.uploaded():
00387     #   log.info("Fmx File 0x%x:%d has already been uploaded" % ( fswFile.fileId(), fswFile.fileId() )
00388     #   return True
00389 
00390     fileID = fswFile.fileID()
00391     uplStr = fswFile.binary()
00392     status = True
00393 
00394     # cancel any pending transfers
00395     # Difficulty with EPUs: cannot upload directly.
00396     #   Must upload to SIU and copy across...
00397     #   Thus, must cancel upload on SIU too.
00398     epuUnits = ( FSW_UNIT_EPU0, FSW_UNIT_EPU1, FSW_UNIT_EPU2, FSW_UNIT_EPU3 )
00399     status = self.cancelUpload(unit)
00400     if unit in epuUnits:
00401       # We're in primary boot in EPU, but not in SIU.  
00402       cachePB = self.__primaryBoot
00403       self.__primaryBoot = False
00404       status &= self.cancelUpload(FSW_UNIT_SIU)
00405       self.__primaryBoot = cachePB
00406     if not status:
00407       print status
00408       raise RuntimeError("Recieved bad status from cancelUpload.  Check previous warning.")
00409 
00410     # start file transfer command
00411     # fiddly code: 1: FSW uploads packets in multiples of 48 bytes.
00412     #              2: filuplstart must tell system *exactly* how much data to expect
00413     #              3: Round up to next 48 byte boundary
00414     dumpSize = len(fswFile.binary())
00415     d,m = divmod(dumpSize, 48)
00416     if m != 0:
00417       d += 1
00418     self.LFILUPLSTART(LFILESIZE=d*48)
00419     status = self.checkLastCommandStatus()
00420     if not status:
00421       raise RuntimeError("Recieved bad status from upload start.  Check previous warning.")
00422 
00423     # loop over 48 byte chunks
00424     nDataBytes = 48
00425     chunk = 0
00426     offset = 0
00427     lastpacket = dumpSize // nDataBytes - 1
00428     while True:
00429       offset = chunk*nDataBytes
00430       if offset >= dumpSize:
00431         break
00432       if lastpacket > 0:
00433         if chunk == 0:
00434           seqFlags = 0x1 # first packet
00435         elif chunk == lastpacket:
00436           seqFlags = 0x2 # last packet
00437         else:
00438           seqFlags = 0x0 # middle packet
00439       else:
00440         seqFlags = 0x3     # not a sequence
00441       self.getCmdObj('LFILUPLDATA').seqFlags = seqFlags
00442       self.__uploadChunk(uplStr[offset:offset+nDataBytes], offset, nDataBytes)
00443       chunk += 1
00444     
00445     # It takes a while for an upload to commit, due to 1553 speed.
00446     # set max timeout to number of upload chunks * 1/8 second plus a fudge factor
00447     uploadTimeout = max(STANDARD_TIMEOUT, chunk*(1./8.)+STANDARD_TIMEOUT)
00448 
00449     # Difficulty with EPUs: cannot upload directly.
00450     #   Must commit to SIU and copy across sith LFILUPLEPU
00451     if unit in epuUnits:
00452       log.debug("Uploading to %s.  Sending to SIU RAM first" % FSW_UNIT_NAMES[unit])
00453       
00454       tempSiuID = FswFileID( device = FSW_DEVICE_RAM,
00455                          directory = 15,
00456                          fileNumber = FswFileID.MASK_FILENUM)
00457       # We're in primary boot in EPU, but not in SIU.  
00458       cachePB = self.__primaryBoot
00459       self.__primaryBoot = False
00460       # commit to SIU
00461       self.LFILUPLCOMMIT( LFILEUNIT=self.__LFSUNIT(FSW_UNIT_SIU),
00462                           LFILEFLAGS=0,
00463                           LFILEID=tempSiuID.id())
00464       status = self.checkLastCommandStatus()
00465       # do the copy
00466       self.LFILUPLEPU(LFILEUNIT=self.__LFSUNIT(unit),
00467                       LFILEPAD16 = 0,
00468                       LFILEID = tempSiuID.id())
00469       status = self.checkLastCommandStatus()
00470       # put primaryBoot flag back.
00471       self.__primaryBoot = cachePB
00472 
00473     # execute commit.  EPU difficulty has already been handled
00474     log.debug("committing to unit:%s, flags:%s, id:%s.  This may take up to %d seconds." % ( unit, 0, str(fileID), uploadTimeout))
00475     self.LFILUPLCOMMIT(LFILEUNIT=self.__LFSUNIT(unit),
00476                        LFILEFLAGS=0,
00477                        LFILEID=fileID.id())
00478     
00479     # when in primary boot, must examine boot hsk for file state.
00480     #   "Primary boot" doesn't just mean "siu primary boot"
00481     #   If SIU is in secondary and EPUx is in primary and we are uploading
00482     #   to EPU, that counts as primary.
00483     if self.__primaryBoot:
00484       pkt, tlm, handler = FILE_PBC_FILESTAT[unit]
00485       self.__fileState = -9999999     # watch variable
00486       vsc = self.getCmdDb().getVsc()
00487       def inlinePBCHandler(telemetry):# Inline handler to get PBC file stat.
00488         try:
00489           pkt = self.getCmdDb().getVsc().getTlmDb().decodeTelemetry(telemetry)
00490           self.__fileState = pkt.get_payload(tlm)
00491         except Exception, e:
00492           self.__fileState = -9999999
00493 
00494       # SIU comes in telem line.  EPUx comes in diag line.
00495       if handler == "diag":
00496         handler = vsc.getDiagHandler()
00497       else:
00498         handler = vsc.getTelemHandler()
00499       pbtTelem = TelemetryEvent(handler,
00500                                 [vsc.getTlmDb().getApidFromName(pkt),],
00501                                 inlinePBCHandler)
00502       # enable async handler and loop until watch variable changes to state 0,
00503       #  or until STANDARD_TIMEOUT.
00504       totWait = 0
00505       pbtTelem.enable()
00506       time.sleep(1)
00507       while self.__fileState != 0:
00508         totWait += 1
00509         time.sleep(1)
00510         if totWait > uploadTimeout:
00511           status = False
00512           break
00513       pbtTelem.disable()
00514     else:
00515       # in Secondary boot, wait for commit message to come back.
00516       status = self.checkLastCommandStatus(timeout = uploadTimeout)
00517       if not status:
00518         raise RuntimeError("Received bad status from File Upload Commit.  Check previous warning")
00519 
00520     # don't forget to tell the file that it has been uploaded
00521     if status: status = fswFile.setUploaded(True)
00522     return status
00523 
00524   def copyFile(self, unit, source, dest, destUnit=None):
00525     """!\brief  Copy a file on any LAT unit
00526 
00527     \param app         A testAppBase object  (your user application...)
00528     \param unit        LAT unit id to upload to
00529     \param source      An FswFileID object corresponding to the source file
00530     \param dest        An FswFileID object corresponding to the destination file
00531     \param destUnit    Unit to copy to (Default = None ==> same unit)
00532     \return status     True if success, False if failure
00533     """
00534     status = True
00535     LFS  = self.getCmdDb().LFS
00536     FILE = self.getCmdDb().FILE
00537     if destUnit is None:
00538       LFS.LFSFILECOPY(LFSUNIT=self.__LFSUNIT(unit),
00539                       LFSSRCFILEID=source.id(),
00540                       LFSDESTFILEID=dest.id(),
00541                       LFSPAD16=0)
00542       status &= self.checkLastCommandStatus()
00543     else:
00544       # copy from SIU to EPU
00545       self.cancelUpload(destUnit)
00546       
00547       self.LFILUPLEPU(LFILEUNIT=self.__LFSUNIT(destUnit),
00548                       LFILEPAD16 = 0,
00549                       LFILEID = source.id())
00550       status &= self.checkLastCommandStatus()
00551       if not status:
00552         log.warning("bad status in copyFile")
00553         return status
00554       time.sleep(5)
00555       self.LFILUPLCOMMIT( LFILEUNIT=self.__LFSUNIT(destUnit),
00556                           LFILEFLAGS = 0,
00557                           LFILEID = dest.id())
00558       status &= self.checkLastCommandStatus()
00559       
00560 
00561     return status
00562 
00563   def deleteFile(self, unit, fileID):
00564     """!\brief Delete a file on a LAT filesystem
00565 
00566     \param app         A testAppBase object  (your user application...)
00567     \param unit        LAT unit id to upload to
00568     \param fileID      An FswFileID object
00569     \return status     True if success, False if failure
00570     """
00571     self.getCmdDb().LFS.LFSFILEDELETE(LFSUNIT=self.__LFSUNIT(unit),
00572                                       LFSFILEID=fileID.id(),
00573                                       LFSPAD16=0)
00574     status = self.checkLastCommandStatus()
00575     return status
00576 
00577   def __LFSUNIT(self, realUnit, tranID=0):
00578     # Actual upload is 4 bits of unit and 12 bits of opaque transaction ID.
00579     return ( (realUnit & 0xf) << 12 | (tranID & 0xfff) )
00580 
00581   def __uploadChunk(self, uplChunk, offset, nDataBytes):
00582     lChunk = len(uplChunk)
00583     if lChunk > nDataBytes:
00584       raise ValueError, "Length of a chunk can't be greater than %d bytes" % nDataBytes
00585     # Pad to nDataBytes bytes
00586     if (nDataBytes-lChunk):
00587       uplChunk = uplChunk + ( chr(0) * (nDataBytes-lChunk) )
00588     l = array.array('B',uplChunk).tolist()
00589     self.LFILUPLDATA(offset, *l)
00590 
00591     # from binascii import hexlify
00592     # print "chunk", offset, hexlify(uplChunk)
00593     # We do not get command confirmation for LFILUPLDATA telecommands
00594 
00595   def checkLastCommandStatus(self, timeout=STANDARD_TIMEOUT):
00596     # boilerplate code that waits for completion on a command
00597     #   and checks the completion status.  If bad, log a warning
00598     status = True
00599     if self.__primaryBoot:
00600       print "WARNING: command completion disabled"
00601       import time
00602       time.sleep(1)
00603       return status
00604     app = self.getCmdDb()
00605     app.waitForConfirmation(timeout)
00606     statusMsg = app.getConfirmationMsg()
00607     if not app.getVsc().getTlmDb().msgIsSuccess(statusMsg):
00608       log.warning(app.getVsc().getTlmDb().msgByValue(statusMsg))
00609       status=False
00610     return status
00611 
00612   def setPrimaryBoot(self, bool):
00613     self.__primaryBoot = bool
00614 
00615 
00616 
00617 
00618 
00619 
00620 
00621 
00622 ######################################################
00623 class FswFile(object):
00624   """!\brief Functional interface to a generic file in FSW
00625   """
00626   def __init__(self, fid, bin, uploaded=False):
00627     """!\brief Constructor
00628 
00629     \param  fid       FswFileID for this file
00630     \param  bin       Raw binary representation of file
00631     \param  uploaded  True if file has been uploaded
00632     """
00633     self.__fid      = fid
00634     self.__bin      = bin
00635     self.__uploaded = uploaded
00636 
00637   def fileID(self):
00638     """!\brief retrieve file ID
00639 
00640     \return a FswFileID object
00641     """
00642     return self.__fid
00643 
00644   def binary(self):
00645     """!\brief retrieve file data
00646 
00647     \return a raw binary string
00648     """
00649     return self.__bin
00650 
00651   def uploaded(self):
00652     """!\brief retrieve file upload status
00653 
00654     \return True if uploaded
00655     """
00656     return self.__uploaded
00657 
00658   def setUploaded(self, bool):
00659     """!\brief Set file upload status
00660 
00661     \param  bool True if file is uploaded
00662     \return True if this succeeds
00663     """
00664     self.__uploaded = bool
00665     return True
00666 
00667   def write(self, stream):
00668     """!\brief write file to stream  (file descriptor)
00669 
00670     \param  stream  Any writable file descriptor
00671     """
00672     stream.write(self.__bin)
00673     stream.flush()    # because of buffering, string may not show up until flush() or close()
00674 
00675 class FswFileFromData(FswFile):
00676   """!\brief A memory resident FSW file interface
00677   """
00678   def __init__(self, fileID, fileDat, uploaded=False):
00679     """!\brief Constructor
00680 
00681     \param fileID    FswFileID for this file
00682     \param fileDat   The file's raw data
00683     \param uploaded  True if file has been uploaded
00684     """
00685     try:
00686       FswFile.__init__(self, fileID, fileDat, uploaded)
00687     except:
00688       raise
00689 
00690 class FswFileFromDisk(FswFile):
00691   """!\brief A disk resident FSW file interface
00692   """
00693   def __init__(self, fileID, fileName, uploaded=False):
00694     """!\brief Constructor
00695 
00696     \param fileID    FswFileID for this file
00697     \param fileName  A fully qualified file name to open
00698     \param uploaded  True if file has been uploaded
00699     """
00700     self.name = fileName
00701     try:
00702       self.file = open(fileName, 'rb')
00703       bin = self.file.read()
00704       FswFile.__init__(self, fileID, bin, uploaded)
00705     except Exception, e:
00706       raise
00707 
00708 
00709 
00710 class FswFileID(object):
00711   """!\brief Define a Flight Software File ID
00712 
00713   Defines the FSW file id, as specified in the FILE FSW traveler document
00714   """
00715   MASK_DEVICE    = 0x7
00716   MASK_DIRECTORY = 0x7F
00717   MASK_FILENUM   = 0x3FFFFF
00718   SHFT_DEVICE    = 29
00719   SHFT_DIRECTORY = 22
00720   SHFT_FILENUM   =  0
00721   def __init__(self, *argv, **argd):
00722     """!\brief Polymorphic constructor
00723 
00724     Takes 1 or 3 arguments {fileID} or {device,directory,fileNumber}
00725     Arguments can be either named or not.  A mix of both named and unnamed
00726     args is verboten.
00727 
00728     \param fileID    A LAT file ID (32 bit packed)
00729 
00730     \param device      Device to store file to [0..7]
00731     \param directory   Directory to store file to [0..0x7F]
00732     \param fileNumber  File Number [0..0x3FFFFF]
00733     """
00734     if len(argv) == 1:
00735       self.__fid = argv[0]
00736     elif len(argv) == 3:
00737       self.__fid = self.__computeFileID(argv[0], argv[1], argv[2])
00738     elif len(argd) == 1:
00739       self.__fid = argd['fileID']
00740     elif len(argd) == 3:
00741       self.__fid = self.__computeFileID( argd['device'], argd['directory'], argd['fileNumber'] )
00742     else:
00743       raise SyntaxError( "Incorrect arguments for FswFileID constructor: argv=%s, argd=%d" % (str(argv), str(argd)) )
00744 
00745   def __computeFileID(self, device, directory, filNum):
00746     """!\brief Compute the 32 bit FSW destination from component parts
00747     """
00748     ### Should we also check the following masks and ranges for __fid specified constructors?
00749 
00750     if device & self.MASK_DEVICE != device:
00751       raise "Selected device number 0x%x is greater than the maximum of 0x7" % device
00752     if directory & self.MASK_DIRECTORY != directory:
00753       raise "Selected directory number 0x%x is greater than the maximum of 0x7F" % directory
00754     if directory not in FSW_DIR_RANGE + [ FSW_DIR_ROOT ]:
00755       raise "Selected directory number 0x%x not in allowed directory range of [0..6f, 7f]" % directory
00756     if filNum & self.MASK_FILENUM != filNum:
00757       raise "Selected file number 0x%x is greater than the maximum of 0x3fffff" % filNum
00758 
00759     dest = (device    << self.SHFT_DEVICE) + \
00760            (directory << self.SHFT_DIRECTORY) + \
00761            (filNum    << self.SHFT_FILENUM)
00762     return dest
00763 
00764   def id(self):
00765     """!\brief retrieve file ID
00766 
00767     \return 32 bit file ID
00768     """
00769     return self.__fid
00770 
00771   def device(self):
00772     """!\brief retrieve device
00773 
00774     \return 3 bit device
00775     """
00776     return (self.__fid >> self.SHFT_DEVICE ) & self.MASK_DEVICE
00777 
00778   def directory(self):
00779     """!\brief retrieve file ID
00780 
00781     \return 7 bit directory
00782     """
00783     return (self.__fid >> self.SHFT_DIRECTORY ) & self.MASK_DIRECTORY
00784 
00785   def fileNumber(self):
00786     """!\brief retrieve file ID
00787 
00788     \return 22 bit file Number
00789     """
00790     return (self.__fid >> self.SHFT_FILENUM ) & self.MASK_FILENUM
00791 
00792   def __str__(self):
00793     return '/%s/d%03d/f%07d' % \
00794            ( FSW_DEVICE_NAMES[self.device()], self.directory(), self.fileNumber())
00795 
00796 
00797 class FswDirectoryItem(object):
00798   """!\brief Representation of an item in an FSW directory
00799 
00800   REVISIT:  Make the members private and member functions?
00801   """
00802   __isdirTxt = [ '-', 'd' ]
00803   __roTxt    = [ 'w', '-' ]
00804   def __init__(self, fileID, ro, size, isdir, time, unit, blocks):
00805     """!\ Constructor
00806 
00807     \param fileID  A FswFileID object
00808     \param ro      file is ReadOnly flag (True/False)
00809     \param size    File size in bytes
00810     \param isdir   file is a directory flag  (True/False)
00811     \param time    File modification time in LAT epoch
00812     \param unit    File unit (i.e. FSW_UNIT_SIU)
00813     \param blocks  REVISIT::  file blocks, maybe unused?
00814     """
00815     self.id     = fileID
00816     self.ro     = ro
00817     self.size   = size
00818     self.isdir  = isdir
00819     self.time   = time
00820     self.unit   = unit
00821     self.blocks = blocks
00822     self.header = None
00823 
00824   def dump(self, stream):
00825     """!\brief dump a directoryItem to a file descriptor.
00826 
00827     \param stream  Any file descriptor  (ie. sys.stdout)
00828     """
00829     msg = ""
00830     msg += self.__isdirTxt[self.isdir]
00831     msg += 'r'                            # always readable
00832     msg += self.__roTxt[self.ro]
00833     msg += ' %4s' % FSW_UNIT_NAMES[self.unit]
00834     msg += ' %10d' % self.size
00835     try:
00836       msg += ' %s' % utcfromtimestamp(int(self.time), 0).ctime()
00837     except:
00838       msg += ' %s' % utcfromtimestamp(0, 0).ctime()
00839     strid = str(self.id)
00840     if self.isdir: strid = strid[:-8]
00841     msg += ' %s' % strid
00842     print >> stream, msg
00843 
00844 
00845 class FswDirectory(object):
00846   """!\brief Representation of a FSW directory
00847 
00848   """
00849   def __init__(self, dirID):
00850     """!\ Constructor
00851 
00852     \param dirID  A FswFileID object
00853     """
00854     self.__id = dirID
00855     self.__files = []
00856 
00857   def addItem(self, f):
00858     """!\brief Add a file to this representation
00859 
00860     \param f  An FswDirectoryItem
00861     """
00862     if f not in self.__files:
00863       self.__files.append(f)
00864 
00865   def files(self):
00866     """!\brief return a list of all files in this directory rep
00867     """
00868     return self.__files
00869 
00870   def id(self):
00871     """!\brief retrieve file ID
00872 
00873     \return a FswFileID object
00874     """
00875     return self.__id
00876 
00877   def setId(self, id):
00878     """!\brief set the ID for this directory
00879 
00880     \param id   A FswFileID object
00881     """
00882     self.__id = id
00883 
00884   def dump(self, stream):
00885     """!\brief dump a directory to a file descriptor
00886 
00887     \param stream  Any file descriptor  (ie. sys.stdout)
00888     """
00889     msg = '/%s/d%03d' % ( FSW_DEVICE_NAMES[self.id().device()], self.id().directory())
00890     print >> stream, msg
00891     for item in self.__files:
00892       item.dump(stream)
00893 
00894 def addFmxHeader(inFile, doCompress=False, preCompressed=False, name=None,
00895                  type=None, key=None, timestamp=None):
00896   """!\brief Add an fmx header to a file.  compressing on the fly if requested
00897 
00898   \param  inFile         An FswFile object
00899   \param  doCompress     True/False  to compress on fly/not
00900   \param  preCompressed  True/False  if already compressed
00901   \param  name
00902   \param  type
00903   \param  key
00904   \param  timestamp
00905 
00906   \return An FswFile object with proper headers for upload
00907   """
00908   if not FILETOOL_HDR_ALLOW:
00909     raise RuntimeError('Adding an FMX header is not possible on non-unix systems')
00910 
00911   hdrCmd  = FSW_ADD_HEADER_CMD
00912   if hasattr(inFile, 'name'):  # passing a disk resident file
00913     rawFile = inFile
00914   else:
00915     rawFile  = tempfile.NamedTemporaryFile(mode='w+b')
00916     inFile.write(rawFile.file)
00917   hdrFile  = tempfile.NamedTemporaryFile(mode='w+b')
00918 
00919 
00920   if doCompress:
00921     # compress in tmpspace
00922     cmpFile  = tempfile.NamedTemporaryFile(mode='w+b')
00923 
00924     cmd  = FSW_COMPRESS_CMD
00925     cmd += ' %s' % rawFile.name
00926     cmd += ' %s' % cmpFile.name
00927     rc = os.system(cmd)
00928     rawFile = cmpFile
00929   if doCompress or preCompressed:
00930     hdrCmd += ' -c'
00931 
00932   # Various options for the header adding command
00933   if name is not None:
00934     hdrCmd += ' -n=%s' % name
00935   if type is not None:
00936     hdrCmd += ' -t=%s' % str(type)
00937   if key is not None:
00938     hdrCmd += ' -k=%s' % str(key)
00939   if timestamp is not None:
00940     hdrCmd += ' -s=%s' % str(timestamp)
00941 
00942   hdrCmd += ' %s' % rawFile.name
00943   hdrCmd += ' %s' % hdrFile.name
00944   rc = os.system(hdrCmd)
00945 
00946   retFile = FswFileFromDisk(inFile.fileID(), hdrFile.name)
00947 
00948   return retFile
00949 
00950 

Generated on Thu Apr 27 20:52:41 2006 for LICOS L02-01-00 by doxygen 1.4.6-NO