Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

SnSDUtils.cpp

Committer:
uci1
Date:
2012-10-10
Revision:
21:ce51bb0ba4a5
Parent:
20:e5857b287b3b
Child:
22:f957c4f840ad

File content as of revision 21:ce51bb0ba4a5:

#include "SnSDUtils.h"

#include "mbed.h"
#include "SDFileSystem.h"
#include "FATDirHandle.h"
#include <string.h>
#include <string>

#include "SnConfigFrame.h"
#include "SnEventFrame.h"

//#define DEBUG

const char* const SnSDUtils::kSDdir     = "/sd";
const char* const SnSDUtils::kSDsubDir  = "/sd/data";
char              SnSDUtils::fgCurFileName[kFNBufSize]={0};
FILE*             SnSDUtils::fgCurFile  = 0;
uint16_t          SnSDUtils::fgCurSeq   = 0;
const uint8_t     SnSDUtils::kIOvers    = 3;
const uint32_t    SnSDUtils::kMaxSizeOfFileHdr = 
    sizeof(uint8_t)+sizeof(uint64_t)+sizeof(uint32_t)+sizeof(uint16_t)
    +(sizeof(uint8_t)+(2u*sizeof(uint16_t))); // power frame v1

static const uint16_t __kMaxUShort = ~0;

const char* SnSDUtils::GetOutFileName(const uint64_t macadr,
                                      const uint32_t run,
                                      const uint16_t seq) {
    // returns the formatted file name, or NULL if the directory is too long
    // and the full name cannot fit in the buffer
    // NOTE: this fcn uses a static buffer, and so should not be called
    // multiple times in the same line (that includes the calling of functions
    // that call this function!)
    //
    // filename = SnEvtsM[6-byte hex mac adr]r[6-digit run num]s[5-digit seq num].dat
    //  35 chars      7     +    12         +1+         5     +1+     5         + 4
    
    if (strlen(kSDsubDir)<(kFNBufSize-37)) {
        static char tbuf[kFNBufSize];
        memset(tbuf, 0, sizeof(char)*kFNBufSize);
        // if file name format changes, GetSeqNum must be changed too
        sprintf(tbuf, "%s/SnEvtsM%012llXr%05ds%05d.dat",
            kSDsubDir,
            macadr>>16, // 64 -> 48 bits
            run, seq);
        return tbuf;
    } else {
        return NULL;
    }
}
/*
uint16_t SnSDUtils::GetCurSeqNum() {
    return GetSeqNumFromFileName(fgCurFileName);
}
*/
uint16_t SnSDUtils::GetSeqNumFromFileName(const char* fn) {
    uint16_t seq=0;
    const uint32_t ncomp = strrchr(fn, 's') - fn;
    if (ncomp<strlen(fn)) {
        sscanf(fn+ncomp,"s%hu.dat",&seq);
    }
    return seq;
}

uint16_t SnSDUtils::GetSeqNum(const uint64_t macadr,
                              const uint32_t run) {
    // count the files having expected filename format
    
    // fn points to a static buffer
    const char* fn = GetOutFileName(macadr, run, 0)
        + strlen(kSDsubDir) + 1; // take out dir and '/'s
    
    DIR* d( opendir(kSDsubDir) );
    if (d==NULL) {
        // try making the directory
        mkdir(kSDsubDir, 0777);
        d = opendir( kSDsubDir );
    }
    struct dirent* dent;
    
    uint16_t seq=0, newseq=0;
    if ( d!=NULL ) {
        // don't compare seq#. don't use num of chars in case seq is >999
        const uint32_t ncomp = strrchr(fn, 's') - fn;
        while ( (dent = readdir(d))!=NULL ) {
            if (strncmp(dent->d_name, fn, ncomp)==0) {
                // allow for deleted files to make gaps.
                // search for highest seq number and increase that
                sscanf((dent->d_name)+ncomp,"s%hu.dat",&seq);
#ifdef DEBUG
                printf("dn=%s, seq=%hu, __kMaxUShort=%hu\r\n",
                    dent->d_name, seq, __kMaxUShort);
#endif
                if (seq==__kMaxUShort) {
                    newseq = seq;
                    break;
                }
                if (seq>=newseq) {
                    newseq=seq+1;
                }
                if (newseq==__kMaxUShort) {
                    break;
                }
            }
        }
        closedir(d);
    }

#ifdef DEBUG
    printf("return newseq=%hu\r\n",newseq);
#endif
    return newseq;
}

FILE* SnSDUtils::OpenExistingFile(const char* name, const bool setcurrent) {
    FILE* f = 0;
    if (name!=NULL) {
        f = OpenSDFile(name, "rb");
        /*
        if (setcurrent) {
            fgCurFile = f;
            strncpy(fgCurFileName, name, kFNBufSize-1);
            fgCurSeq = GetSeqNumFromFileName(fgCurFileName);
        }
        */
    }
    return f;
}

FILE* SnSDUtils::OpenSDFile(const char* name, const char* mode) {
    // TODO: check if we have memory?
    std::string fn(name);
    if (strncmp(name, kSDsubDir, strlen(kSDsubDir))!=0) {
        // filename lacks directory
        fn = kSDsubDir;
        fn += "/";
        fn += name;
    }
#ifdef DEBUG
    printf("OpenSDFile: %s, mode %s\r\n",fn.c_str(),mode);
#endif
    FILE* f = fopen(fn.c_str(), mode);
    //setvbuf(f, 0, _IONBF, 0); // no buffering
#ifdef DEBUG
    printf("OpenSDFile: f=%p\r\n",(void*)f);
#endif
    return f;
}

FILE* SnSDUtils::OpenNewOutputFile(const uint64_t macadr,
                                   const uint32_t run) {
    // opens a new file in the specified directory and writes this
    // this mbed's mac address as the first sizeof(uint64_t) bytes (i.e. 4 bytes)
    //
    fgCurSeq = GetSeqNum(macadr, run);
    memset(fgCurFileName, 0, sizeof(char)*kFNBufSize);
    strncpy(fgCurFileName,GetOutFileName(macadr, run, fgCurSeq),kFNBufSize-1);
    //fprintf(stderr,"cur file = %s (%hu)\n\r",fgCurFileName,fgCurSeq);
    fgCurFile = 0;
    if (fgCurFileName!=NULL) {
        fgCurFile = OpenSDFile(fgCurFileName, "wb");
        if (fgCurFile!=NULL && ferror(fgCurFile)==0) {
#ifdef DEBUG
            printf("Writing file header\r\n");
#endif
            WriteFileHeader(fgCurFile, macadr, run, fgCurSeq);
        }
    }
#ifdef DEBUG
    printf("fgCurFile=%p\r\n",(void*)fgCurFile);
#endif
    return fgCurFile;
}

void SnSDUtils::GetDirProps(const char* dirname,
                            uint32_t& nfiles,
                            float& totbytes) {
    nfiles = 0;
    totbytes = 0;
    FATDirHandle* d = dynamic_cast<FATDirHandle*>( opendir(dirname) );
    if (d!=0) {
        while ( (readdir(d))!=NULL ) {
            ++nfiles;
            totbytes += d->filinfo()->fsize;
        }
        closedir(d);
    }
#ifdef DEBUG
    printf("GetDirProps: %s :: nf=%u, tb=%u\r\n",
        dirname, nfiles, totbytes);
#endif
}

bool SnSDUtils::WriteEventTo(FILE* efile, char* const evtBuf,
                             const SnEventFrame& evt,
                             const SnConfigFrame& conf) {
    // write event to SD card
        
    uint8_t sLoseLSB=0, sLoseMSB=0;
    uint16_t sWvBase=0;
    conf.GetPackParsFor(SnConfigFrame::kSDcard, sLoseLSB, sLoseMSB, sWvBase);
    SnHeaderFrame::WriteTo(efile, SnHeaderFrame::kEventCode, 
                           evt.SizeOf(SnEventFrame::kIOVers, sLoseLSB, sLoseMSB));
    const bool ret = evt.WriteTo(efile, evtBuf, sLoseLSB, sLoseMSB, sWvBase);
    fflush(efile);
    return ret;
}

bool SnSDUtils::WriteConfig(FILE* efile,
                            const SnConfigFrame& conf) {
    SnHeaderFrame::WriteTo(efile, SnHeaderFrame::kConfigCode, 
                           conf.SizeOf(SnConfigFrame::kIOVers));
    conf.WriteTo(efile);
    return true;
}

void SnSDUtils::DeleteFile(FILE*& f, const char* fname) {
    fclose(f);
    f=0;
    // make sure we're trying to delete a file off the SD card
    std::string fn(fname);
    if (strncmp(fname,kSDsubDir,strlen(kSDsubDir))!=0) {
        fn  = kSDsubDir;
        fn += "/";
        fn += fname;
    }
    remove(fn.c_str());
}

SnCommWin::ECommWinResult SnSDUtils::SendAllFiles(SnCommWin* comm,
                                                  const uint32_t timeout,
                                                  char* const buf,
                                                  const uint32_t bsize,
                                                  const SnConfigFrame& curConf,
                                                  SnEventFrame& evt,
                                                  SnPowerFrame& pow,
                                                  const uint32_t handshakeTimeout) {

    DIR* d;
    struct dirent* dent;
    
    SnCommWin::ECommWinResult rs  = SnCommWin::kOkMsgSent;
    
    if ( (d = opendir( kSDsubDir ))!=NULL ) {
        FILE* f;
        while ( (dent = readdir(d))!=NULL ) {
            if (strncmp(dent->d_name, "SnEvts", 6)==0) {
                const bool isCurFile = 
                    (strcmp(dent->d_name, GetCurFileName())==0);
                if (isCurFile) {
                    // file must already be written out!
                    f = GetCurFile();
                } else {
                    f = OpenExistingFile(dent->d_name, false);
                }
#ifdef DEBUG
                printf("calling senddata: f=%p (cur %p), fn=%s\r\n",
                    f, GetCurFile(), dent->d_name);
#endif
                const SnCommWin::ECommWinResult res = 
                    comm->SendData(f, dent->d_name,
                                   curConf, evt, pow, buf, bsize,
                                   0, timeout, handshakeTimeout);
                if (res<rs) {
                    rs = res;
                }
                // don't stop if res is bad. don't want one bad file to
                // prevent sending the others
                
                // send data should close or delete the file (if appropriate)
                // unless it's the current file
                if (isCurFile) {
                    // move (back) to the end of the file
                    // altho hopefully no writing will happen after this
                    fseek(fgCurFile, 0, SEEK_END);
                }
            }
        }
        closedir(d);
    }
    
    return rs;
}