Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

SnCommWin.cpp

Committer:
uci1
Date:
2019-06-05
Revision:
125:ce4045184366
Parent:
122:c1b5023eac69

File content as of revision 125:ce4045184366:

#include "SnCommWin.h"

#include "SnHeaderFrame.h"
#include "SnConfigFrame.h"
#include "SnEventFrame.h"
#include "SnPowerFrame.h"
#include "SnStatusFrame.h"
#include "SnSignalStrengthFrame.h"
#include "SnSDUtils.h"
#include "SnConstants.h"
#include "SnCRCUtils.h"
#include "SnCommPeripheral.h"
#include "SnClockSetFrame.h"
#include "SnHeartbeatFrame.h"

#include <algorithm>
#include <ctype.h>
extern "C" void mbed_reset();

//#define DEBUG

const char*   SnCommWin::kLocalDir = "/local";
const char*   SnCommWin::kDelAllConfCodeStr = "fj2io32FIJ(#jd;;.O@++/]ewavk2[49ojv";
const uint8_t SnCommWin::kDelAllConfCodeLen = 35; // length of the above string

SnCommWin::SnCommWin(SnCommPeripheral* p) :
    fSendingInHandshake(false), fComm(p) {
    if (p==0) {
        error("SnCommWin - must have peripheral! Received 0.\r\n");
    }
}

SnCommWin::~SnCommWin() {
    delete fComm;
}

bool SnCommWin::TrySetSysTimeUnix(const uint32_t timeout,
                                  uint32_t& prvTime,
                                  uint32_t& setTime) {
    return (fComm!=0) ? (fComm->TrySetSysTimeUnix(timeout,prvTime,setTime)) 
                      : false;
}

bool SnCommWin::Connect(const uint32_t timeout) {
    Watchdog::kick(); // don't reset
    return (fComm!=0) ? (fComm->Connect(timeout)) : false;
}

bool SnCommWin::CloseConn(const uint32_t timeout,
                          char* const genBuf/*=0*/,
                          const bool sendCloseSignal/*=false*/) {
    Watchdog::kick(); // don't reset
    bool closeOk = false;
    if (fComm!=0) {
        if (sendCloseSignal && (genBuf!=0)) {
            char* b = genBuf;
            SnHeaderFrame::WriteTo(b, SnHeaderFrame::kCommSignOffCode, 0);
            const uint32_t bytesToBeSent = b-genBuf;
            const int32_t mlen = fComm->SendAll(genBuf, bytesToBeSent, timeout);
            const int32_t flen = fComm->FinishSending(timeout);
            closeOk = (bytesToBeSent==(mlen+flen));
        } else {
            closeOk = true;
        }
        closeOk &= fComm->CloseConn(timeout);
    }
    return closeOk;
}

bool SnCommWin::PowerDown(const uint32_t timeout) {
    Watchdog::kick(); // don't reset
    return (fComm!=0) ? (fComm->PowerDown(timeout)) : false;
}

SnCommWin::ECommWinResult SnCommWin::SendData(SnConfigFrame& conf,
                                              SnEventFrame& evt,
                                              SnPowerFrame& pow,
                                              char* const genBuf,
                                              const uint32_t bsize,
                                              const uint32_t timeout) {
#ifdef DEBUG
    printf("SnCommWin::SendData\r\n");
#endif
    Watchdog::kick(); // don't reset

    ECommWinResult res = kUndefFail;
    if ( (GetCommType() != SnConfigFrame::kIrid) ||
         (conf.IsForcingSBDdata()) ) {
         // only send large amounts if we're not communicating by Iridum,
         // or if we are being forced to send the data over SBD
        if (conf.IsSendingAllFiles()) {
#ifdef DEBUG
            printf("sending all files\r\n");
#endif
            res = SnSDUtils::SendAllFiles(this, timeout, 
                                          genBuf, bsize, conf, evt, pow);
            // handshakes handled after each file, in SendDataFromFile
        } else {
            if (conf.GetCommSendData()==0) {
#ifdef DEBUG
                printf("no data to send\r\n");
#endif
                res = kOkNoMsg;
            } else if (conf.IsSendingFilesRunSeqList()) {
#ifdef DEBUG
                printf ("calling SendFilesInRunSeqList\r\n");
#endif
                res = SnSDUtils::SendFilesInRunSeqList(
                               this, timeout, 
                               genBuf, bsize, conf, evt, pow);
#ifdef DEBUG
                printf ("res = %d after SendFilesInRunSeqList\r\n",
                    static_cast<int>(res));
#endif
            } else {
                // send most recent file or a few events from that file
                const uint32_t nev = (conf.GetCommSendData()>0) ?
                                      conf.GetCommSendData() // send N events
                                      : 0u; // send all events in last file
#ifdef DEBUG
                printf("calling SendDataFromFile. f=%s, nev=%u\r\n",
                        SnSDUtils::GetCurFileName(), nev);
#endif
                uint8_t hndcode(0);
                uint32_t hndlen(0);
                res = SendDataFromFile(SnSDUtils::GetCurFile(), SnSDUtils::GetCurFileName(),
                               conf, evt, pow, genBuf, bsize, nev, timeout,
                               &hndcode, &hndlen);
                // handshake handled in SendData
#ifdef DEBUG
                printf("after send data cur file, res=%d\r\n",(int)res);
#endif
            }
        }
    } else {
        return kOkNoMsg;
    }
    return res;
}

SnCommWin::ECommWinResult SnCommWin::SendDataFromFile(FILE* inf, const char* infn,
                                              const SnConfigFrame& curConf,
                                              SnEventFrame& evt,
                                              SnPowerFrame& pow,
                                              char* const genBuf,
                                              const uint32_t bsize,
                                              const uint32_t nevts,
                                              const uint32_t timeout,
                                              uint8_t* hndcode,
                                              uint32_t* hndlen) {
    // nevts==0 ==> send all events
    // handshakeTimeout is never a clock time -- always a delta(t)
    
#ifdef DEBUG
    printf("SnCommWin::SendDataFromFile file (%p), fn=%s\r\n",inf,infn);
#endif
    Watchdog::kick(); // don't reset
    
    ECommWinResult res = kUndefFail;
    bool didDel = false;

    // allow up to the full window if we're not obeying timeout
    const uint32_t timeout_clock = curConf.IsObeyingTimeout() ?
                                    timeout :
                                    curConf.GetTimeoutTime(time(0),
                                        curConf.GetCommWinDuration());
        

    if (inf!=0) {
#ifdef DEBUG
        printf("sending file name %s\r\n",infn);
#endif
        
        // send the file name
        int32_t btogo(0), ttogo(0);
        int32_t bsent = SendFilename(infn, genBuf, btogo, timeout_clock);
        if (genBuf!=0) {
#ifdef DEBUG
            printf("calling send file contents\r\n");
#endif

            // send the contents
            bsent += SendFileContents(inf, curConf, evt, pow, genBuf,
                                      nevts, ttogo, timeout_clock);
            btogo += ttogo;
            bsent += fComm->FinishSending(timeout_clock);
            res = (bsent==btogo) ? SnCommWin::kOkMsgSent 
                                 : SnCommWin::kFailPartSent;
#ifdef DEBUG
            printf("res=%d (fails=%d)\r\n",(int)res,(int)kAllFails);
#endif
            if (res>kAllFails) {
#ifdef DEBUG
                printf("calling wait handshake\r\n");
#endif
                Watchdog::kick(); // don't reset
                
                // wait for handshake
                uint8_t hndshk(0); uint32_t hndshkLen(0);
                res = WaitHandshake(curConf, timeout_clock, genBuf, bsize, hndshk,
                                    &hndshkLen);
                if (hndcode!=0) {
                    *hndcode = hndshk;
                }
                if (hndlen!=0) {
                    *hndlen = hndshkLen;
                }
#ifdef DEBUG
                printf("res=%d, nevts=%u, isdel=%d, inf=%p, curfile=%p, hndshk=%02x\r\n",
                    res, nevts, curConf.IsDeletingFiles(), inf,
                    SnSDUtils::GetCurFile(), hndshk);
#endif
                if (SnCommWin::kOkWithMsg==res) {
                    res = HandleHandshake(
                          //SnSDUtils::GetCurFile(), SnSDUtils::GetCurFileName(),
                          inf, infn,
                          curConf, evt, pow, genBuf, bsize, timeout_clock,
                          hndshk, hndshkLen, &nevts);
                    didDel = (SnCommWin::kOkWthMsgDidDel==res);
                }
            }
        } // if genBuf!=0
    } else {
#ifdef DEBUG
        printf("inf=0!\r\n");
#endif
        res = kOkNoMsg;
    }
    if ( (inf!=SnSDUtils::GetCurFile()) && (didDel==false) ) {
        SnSDUtils::CloseOutputFile(inf);
    }
    if ( fComm->IsTimedOut(timeout_clock) ) {
        if ( kFailTimeout < res ) {
            res = kFailTimeout;
        }
    }
    return res;
}

SnCommWin::ECommWinResult SnCommWin::HandleHandshake(FILE* inf, const char* infn,
                                                     const SnConfigFrame& curConf,
                                                     SnEventFrame& evt,
                                                     SnPowerFrame& pow,
                                                     char* const genBuf,
                                                     const uint32_t bsize,
                                                     const uint32_t handshakeTimeout,
                                                     const uint8_t hndshk,
                                                     const uint32_t hndlen,
                                                     const uint32_t* nevts) {

    SnCommWin::ECommWinResult res = SnCommWin::kUnexpectedRec;
    
    Watchdog::kick(); // don't reset
    
    if (   (hndshk==SnHeaderFrame::kHnShNoReplyCode)
        || (hndshk==SnHeaderFrame::kHnShOkPartlCode) ) {
        // nothing to do
        res = SnCommWin::kOkWithMsg;
    } else if (hndshk==SnHeaderFrame::kHnShFailNonCode) {
        res = SnCommWin::kFailNoneSent;
    } else if (hndshk==SnHeaderFrame::kHnShFailPrtCode) {
        res = SnCommWin::kFailPartSent;
    } else if (hndshk==SnHeaderFrame::kHnShOkComplCode) {
        // file received
#ifdef DEBUG
        printf("HnShOkCompl: isdel=%d, isCurFil=%d (%p,%p), nevts_ptr=%p\r\n",
            (int)(curConf.IsDeletingFiles()),
            (int)(inf==SnSDUtils::GetCurFile()),
            (void*)inf, (void*)(SnSDUtils::GetCurFile()),
            (void*)(nevts));
        if (nevts!=0) {
            printf("*nevts=%u\r\n",*nevts);
        }
#endif
        if (   (curConf.IsDeletingFiles())
            && (inf!=SnSDUtils::GetCurFile())
            && ((nevts!=0) && (*nevts==0)) ) { // whole file was received
            // delete this file
#ifdef DEBUG
            printf("deleting file %p, %s\r\n",inf,infn);
#endif
            SnSDUtilsWhisperer::DeleteFile(inf, infn);
            res = SnCommWin::kOkWthMsgDidDel;
        } else {
            res = SnCommWin::kOkWithMsg;
        }
    } else if (  hndshk==SnHeaderFrame::kHnShOkStopCode ) {
        res = SnCommWin::kOkStopComm;
    } else if ( (hndshk==SnHeaderFrame::kHnShOkDelAlCode) ||
                (hndshk==SnHeaderFrame::kHnShOkDelRnCode) ) {
        if ( GetDeleteAllConfirmCode(curConf, 
                                     kDelAllConfCodeLen,
                                     handshakeTimeout, 
                                     genBuf, bsize) ) {
            if (hndshk==SnHeaderFrame::kHnShOkDelAlCode) {
#ifdef DEBUG
                printf("delete all\r\n");
#endif
                SnSDUtilsWhisperer::DeleteAllFiles();
            } else if (hndshk==SnHeaderFrame::kHnShOkDelRnCode) {
#ifdef DEBUG
                printf("delete run %u\r\n",hndlen);
#endif
                SnSDUtilsWhisperer::DeleteFilesOfRun(hndlen);
            }
            res = SnCommWin::kOkStopComm;
        }
    } else if ( hndshk==SnHeaderFrame::kHnShOkClRSLCode ) {
#ifdef DEBUG
        printf("clear run seq list requested\r\n");
#endif
        SnSDUtils::ClearRunSeqList(curConf.IsSendingFilesRunSeqList());
    } else if ( hndshk==SnHeaderFrame::kHnShOkReqRnCode ) {
#ifdef DEBUG
        printf("run %u requested\r\n",hndlen);
#endif
        if (fSendingInHandshake==false) {
            // prevent another SendAllOfRun handshake request until we're done
            // (allowing it could lead to a stack overflow)
            fSendingInHandshake = true;
            SnSDUtils::SendAllOfRun(this, handshakeTimeout,
                                    genBuf, bsize, curConf, evt, pow,
                                    hndlen);
            fSendingInHandshake = false;
            res = SnCommWin::kOkStopComm;
        }
    } else if ( hndshk==SnHeaderFrame::kHnShOkRqPtRnCode ) {
#ifdef DEBUG
        printf("part of run %u requested\r\n",hndlen);
#endif
        // need the seq limits -- another handshake message
        SendHndshkReq(genBuf, handshakeTimeout);
        uint8_t shndshk(0); uint32_t shndshkLen(0);
        res = WaitHandshake(curConf, handshakeTimeout, genBuf, bsize, shndshk,
                            &shndshkLen);
        if (SnCommWin::kOkWithMsg==res) {
            if ( shndshk==SnHeaderFrame::kHnShOkRqPtSqCode ) {
                // ok got the min/max seq's as expected
                const uint16_t minseq = GetMinSeqFrom(shndshkLen);
                const uint16_t maxseq = GetMaxSeqFrom(shndshkLen);
#ifdef DEBUG
                printf("[minseq=%hu, maxseq=%hu]\r\n",minseq,maxseq);
#endif
                if (fSendingInHandshake==false) {
                    // prevent another SendAllOfRun handshake request
                    // until we're done
                    // (allowing it could lead to a stack overflow)
                    fSendingInHandshake = true;
                    SnSDUtils::SendPartOfRun(this, handshakeTimeout,
                                        genBuf, bsize, curConf, evt, pow,
                                        hndlen, // the run num
                                        minseq, maxseq);
                    fSendingInHandshake = false;
                }
            }
        }
        res = SnCommWin::kOkStopComm;
    }
    
    return res;
}


bool SnCommWin::GetDeleteAllConfirmCode(const SnConfigFrame& conf,
                                        const uint32_t length,
                                        const uint32_t timeout,
                                        char* const buf,
                                        const uint32_t bsize) {
    bool ret = false;
#ifdef DEBUG
    printf("GetDeleteAllConfirmCode, timeout=%u\r\n",timeout);
#endif
    Watchdog::kick(); // don't reset

    // better pull all the bytes no matter the buffer size
    const uint32_t ll = (bsize<length) ? bsize : length;
    int mlen = 0;
    while ( mlen<length ) {
        mlen += fComm->ReceiveAll(buf, ll, timeout);
        if (fComm->IsTimedOut(timeout)) {
            break;
        }
    }

    Watchdog::kick(); // don't reset

    if (mlen==length) {
        const char* b = buf;
        const char* c = kDelAllConfCodeStr;
        bool match = true;
        for (uint8_t i=0; (i<kDelAllConfCodeLen) && match; ++i, ++b, ++c) {
            match = ((*b)==(*c));
        }
        ret = match;
    }
    return ret;
}

SnCommWin::ECommWinResult SnCommWin::WaitHandshake(const SnConfigFrame& conf,
                                                   const uint32_t timeout,
                                                   char* const buf,
                                                   const uint32_t bsize,
                                                   uint8_t& hndShkCode,
                                                   uint32_t* hndShkLen) {
#ifdef DEBUG
    printf("WaitHandshake, timeout=%u\r\n",timeout);
#endif
    
    Watchdog::kick(); // don't reset
    
    const int mlen = fComm->ReceiveAll(buf, SnHeaderFrame::SizeOf(), timeout);
    if (mlen>0 && static_cast<uint32_t>(mlen) == SnHeaderFrame::SizeOf()) {
        uint32_t msgLen=0;
        const char* b = buf;
        SnHeaderFrame::ReadFrom(b, hndShkCode, msgLen);
#ifdef DEBUG
        printf("got handshake code=%02x, len=%u\r\n",hndShkCode,msgLen);
#endif
        if ((hndShkCode & SnHeaderFrame::kHndShkBits)!=0) {
            if (hndShkLen!=0) {
                *hndShkLen = msgLen;
            }
            return SnCommWin::kOkWithMsg;
        } else {
            // TODO: somehow handle unexpected message?
            return SnCommWin::kUnexpectedRec;
        }
    }
    return SnCommWin::kOkNoMsg;
}

SnCommWin::ECommWinResult SnCommWin::GetFilename(const uint32_t timeout,
                                                 char* const buf,
                                                 const uint32_t namelen) {
    // called by GetConfig upon receipt of a kMbedFilenameCode
    
#ifdef DEBUG
    printf("GetMbedFile, to=%u\r\n",timeout);
#endif
    
    Watchdog::kick(); // don't reset
    
    const int mlen = fComm->ReceiveAll(buf, namelen, timeout);
    if (mlen>0 && static_cast<uint32_t>(mlen) == namelen) {
        return SnCommWin::kOkWithMsg;
    }
    return SnCommWin::kUnexpectedRec;
}

bool SnCommWin::BuildLocalFileName(std::string fname,
                                   const char* const dir,
                                   std::string& lfname) {
    // make the file name ALLCAPS, since LocalFileSystem will do it anyway
    SnCommPeripheral::CapitalizeInPlace(fname.begin(), fname.end());
    // now ensure the file name is 8.3 only -- for LocalFileSystem
    const size_t ldlen = strlen(dir);
    // 12 = 8.3 filename format, 1 for / and 1 for \0
    const uint32_t fbs = ldlen+1+12+1;
    char fnb[fbs];
    memset(fnb, 0, sizeof(char)*fbs);
    
    Watchdog::kick(); // don't reset
    
    // find the extension
    const size_t xdot = fname.rfind('.');
    if (xdot!=string::npos && (fname.size()==(4+xdot))) {
        const size_t fnpl = (xdot>8) ? 8 : xdot;
        char* fb = fnb;
        memcpy(fb, dir, ldlen); fb+=ldlen;        // /local
        *fb = '/'; ++fb;                                //       /
        strncpy(fb, fname.c_str(), fnpl); fb+=fnpl;     //        FILENAME
        *fb = '.'; ++fb;                                //                .
        strncpy(fb, fname.c_str()+xdot+1, 3);           //                 EXT
        // success
        lfname = fnb;
        return true;
    }
 #ifdef DEBUG
    printf("filname mangled. (%s) size=%u, xdot=%u",
        fname.c_str(), fname.size(), xdot);
#endif            
   return false;  // fail
}

SnCommWin::ECommWinResult SnCommWin::GetLocalFile(std::string fname,
                                                  char* const buf,
                                                  const uint32_t bsize,
                                                  const uint32_t timeout,
                                                  std::string& lfname) {
    // get a file and save it locally with the specified name
    
    Watchdog::kick(); // don't reset
    
    // get the header for the file
    uint8_t mcode=0; uint32_t mlen=0;
    SnCommWin::ECommWinResult res = 
        GetHeader(timeout, buf, bsize, mcode, mlen);
#ifdef DEBUG
    printf("Got header code %x, len %u\r\n",mcode,mlen);
#endif

    if ( (res>=kOkWithMsg) && (mcode==SnHeaderFrame::kMbedFileCode) ) {
#ifdef DEBUG
        printf("Got mbed file header. File length = %u\r\n",mlen);
#endif
        // got the header.. make the file..
        if ( BuildLocalFileName(fname, kLocalDir, lfname) ) {
            Watchdog::kick(); // don't reset
            
            // all that just for the file name!
            FILE* lf  = fopen(lfname.c_str(),"wb");
#ifdef DEBUG
            printf("tried to open file [%s]. lf=%p\r\n",lfname.c_str(),(void*)lf);
#endif
            // get all the data and dump it into the file
            int b = 0, toget = 0;
            while ( mlen>b ) {
                if (fComm->IsTimedOut(timeout)) {
#ifdef DEBUG
                    printf("timeout while getting file\r\n");
#endif
                    res = kFailTimeout;
                    break;
                }
                toget = mlen - b;
                if (toget>bsize) {
                    toget = bsize;
                }
                const int got = fComm->ReceiveAll(buf, toget, timeout);
                if (lf!=NULL) {
#ifdef DEBUG
                    printf("writing %d bytes to file\r\n", got);
#endif
                    Watchdog::kick(); // don't reset
                    
                    SnBitUtils::WriteTo(lf, buf, got);
                }
                b += got;
            } // file data receive loop
            
            Watchdog::kick(); // don't reset
            
            uint32_t crc=0;
            if (lf!=NULL) {
                // calculate the crc from what's actually in the file
                fclose(lf); // to flush it
#ifdef DEBUG
                printf("fopen: %s\r\n",lfname.c_str());
#endif
                lf  = fopen(lfname.c_str(),"rb");
                if (lf!=0) {
                    fseek(lf, 0, SEEK_END);
                    int32_t fend = ftell(lf);
                    fseek(lf, 0, SEEK_SET);
                    char c;
                    for (int32_t i=0; i<fend; ++i) {
                        
                        Watchdog::kick(); // don't reset
                        
                        SnBitUtils::ReadFrom(lf, c);
                        if (feof(lf)==0 && ferror(lf)==0) {
                            crc = SnCRCUtils::update_crc32_xfer(crc, c);
                        } else {
                            break;
                        }
                    }
                    fclose(lf);
                }
#ifdef DEBUG
                printf("closed file. crc = %u\r\n", crc);
#endif
            } // if output file not 0
#ifdef DEBUG
            printf("mlen=%u, b=%d, eq=%s\r\n",mlen,b,
                (mlen==b)?"true":"false");
#endif
            if ( mlen!=b ) {
                if (res > kUnexpectedRec) {
                    res = kUnexpectedRec;
                } // otherwise it's already worse
            } else {
                
                Watchdog::kick(); // don't reset
                
                // check that the file is ok
                // get the checksum
                res = GetHeader(timeout, buf, bsize, mcode, mlen);
                if ( (res>=kOkWithMsg) && (mcode==SnHeaderFrame::kMbedFileChksCode) ) {
#ifdef DEBUG
                    printf("received chksum=%u, crc=%u, eq=%s\r\n",
                        mlen,crc,(crc==mlen)?"true":"false");
#endif
                    if (crc != mlen) {
#ifdef DEBUG
                        printf("checksums differ! unexpected.\r\n");
#endif
                        res = kUnexpectedRec; // important!
                    }
                }
            } // end get remote file checksum

            if (lf!=NULL) {
                if ( res<kOkWithMsg ) {
                    // timeout, bad checksum, something else bad?
#ifdef DEBUG
                    printf("removing %s\r\n",lfname.c_str());
#endif
                    remove(lfname.c_str()); // delete the file
                } else {
                    // if we got a new program, remove the old ones.
                    // (schedule myself for immediate de-resolution)
                    //
                    // first just count how many we would save
                    const uint16_t nSavedBins = LoopLocalDirBinFiles(false, fname);
#ifdef DEBUG
                   printf("nSavedBins=%hu",nSavedBins);
#endif
                    if (nSavedBins==1) {
                        // ok. new program will be the only one. remove the others
                        // hope the new program works!
                        LoopLocalDirBinFiles(true, fname);

#ifdef DEBUG
                       printf("rebooting\r\n");
#endif                    
                        // goodbye cruel world, it's over. walk on by...
                        mbed_reset();
                    }
                } // end check new program block
            } // if output file not 0
        } else {
            // filename mangled. either doesn't contain a '.'
            // or the extension is longer than 3 letters
            res = SnCommWin::kUnexpectedRec;
        }
    } // if header ok
    
    return res;
}

int16_t SnCommWin::LoopLocalDirBinFiles(const bool doRemove,
                                        const std::string& fname) {
    // loop over the local directory, count the BIN files that match 'fname'
    //
    // fname is assumed to be all caps already!
    //
    // if doRemove==true, will actually delete non-matching files
    //
    // return number of non-matching files
    
    Watchdog::kick(); // don't reset
    
    uint16_t nSavedBins = 0;
    DIR* d( opendir(kLocalDir) );
    struct dirent* dent;
    if ( d!=NULL ) {
        while ( (dent = readdir(d))!=NULL ) {
            
            Watchdog::kick(); // don't reset
            
            const size_t flen = strlen(dent->d_name);
            const char*  dext = dent->d_name + flen - 4;
            if ( strncmp(dext,".BIN",4)==0 || strncmp(dext,".bin",4)==0 ) {
                // ends with .BIN
                if (strncmp(dent->d_name, fname.c_str(), fname.size())!=0) {
                    // some other mbed program than our new one
                    std::string dfn(kLocalDir);
                    dfn += "/";
                    dfn += dent->d_name;
                    if (doRemove) {
                        remove(dfn.c_str());
                    }
                } else {
                    ++nSavedBins;
                }
            } // else not an mbed program
        }
        closedir(d);
    }
    return nSavedBins;
}

SnCommWin::ECommWinResult SnCommWin::GetHeader(const uint32_t timeOut,
                                               char* const buf,
                                               const uint32_t bsize,
                                               uint8_t& mcode,
                                               uint32_t& mlen) {
    SnCommWin::ECommWinResult res = SnCommWin::kUndefFail;
    
    Watchdog::kick(); // don't reset
    
    if (bsize>=SnHeaderFrame::kMaxSizeOf) {
        // get header
        const int hlen = fComm->ReceiveAll(buf, SnHeaderFrame::SizeOf(), timeOut);
        if (hlen>0 && static_cast<uint32_t>(hlen)==SnHeaderFrame::SizeOf()) {
            mcode=0; 
            mlen=0;
            const char* b = buf;
            SnHeaderFrame::ReadFrom(b, mcode, mlen);
#ifdef DEBUG
            printf("mcode=%02x, mlen=%u\r\n", mcode, mlen);
#endif
            res = SnCommWin::kOkWithMsg;
        }
    }
    return res;
}

SnCommWin::ECommWinResult SnCommWin::GetConfig(SnConfigFrame& conf,
                                               const uint32_t timeOut,
                                               char* const confBuf,
                                               const uint32_t bsize) {
    // confBuf assumed to alread be of allocated size
    
#ifdef DEBUG
    printf("GetConfig, to=%u\r\n",timeOut);
#endif
    
    Watchdog::kick(); // don't reset
    
    SnCommWin::ECommWinResult res = SnCommWin::kUndefFail;
    if (bsize>=SnConfigFrame::kMaxSizeOf) {
        // get header
        uint8_t mcode=0; uint32_t mlen=0;
        res = GetHeader(timeOut, confBuf, bsize, mcode, mlen);
        if (res>=SnCommWin::kOkWithMsg) {
            if (mcode==SnHeaderFrame::kNoConfigCode) {
                // no config to get
                res = SnCommWin::kOkWthMsgNoConf;
            } else if (mcode==SnHeaderFrame::kMbedFilenameCode) {
                res = GetFilename(timeOut, confBuf, mlen);
                if (res>kAllFails) {
                    std::string fname(confBuf, mlen); // the filename
                    fname = fname.c_str(); // mlen may include the \0 which would be counted in size()
#ifdef DEBUG
                    printf("got filename = [%s] (%u)\r\n", fname.c_str(), fname.size());
#endif  
                    std::string lfname;
                    res = GetLocalFile(fname, confBuf, bsize, timeOut, lfname);
                    
#ifdef LOAD_DEFAULT_CONFIG_FROM_SD
                    const size_t flen = strlen(fname.c_str());
                    const char*  dext = fname.c_str() + flen - 4;
                    if ( strncmp(dext,".BIN",4)!=0 || strncmp(dext,".bin",4)!=0 ) {
                        // does NOT end with .BIN
                        // copy it to the SD card
                        std::string sdfname;
                        if ( BuildLocalFileName(fname, SnSDUtils::kSDdir, sdfname) ) {
                            SnSDUtils::CopyFileToSD(lfname.c_str(), sdfname.c_str(),
                                                    confBuf, bsize);
                        }
                    }
#endif
                }
            } else if (mcode!=SnHeaderFrame::kConfigCode) {
                res = SnCommWin::kUnexpectedRec;
            } else {
                // get config
                const int clen = fComm->ReceiveAll(confBuf, mlen, timeOut);
                if (clen>0 && static_cast<uint32_t>(clen)==mlen) {
                    const uint8_t Rv = SnConfigFrame::PeekIOversion(confBuf);
                    if (SnConfigFrame::IsIOversionOk(Rv)) {
                        const char* b = confBuf;
                        conf.ReadFrom(b);
                        res = SnCommWin::kOkWithMsg;
                    } else {
                        res = SnCommWin::kUnexpectedRec;
                        char s[256];
                        sprintf(s,"Cannot accept config version [%hhu]. "
                                  "Expect [%hhu].",Rv,SnConfigFrame::kIOVers);
                        SendString(s, timeOut);
                    }
                } else {
                    res = SnCommWin::kUnexpectedRec;
                }
            }
        } else {
            // not a problem if we get nothing (or no config)
            res = SnCommWin::kOkNoMsg;
        }
    }
    return res;
}

int32_t SnCommWin::SendHndshkReq(char* const genBuf,
                                 const uint32_t timeout_clock) {
    // send a request demanding a handshake
    // as we will be waiting for a response after this,
    // FinishSending is also called
    
    Watchdog::kick(); // don't reset
    
    char* b = genBuf;
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kHnShDemandCode, 0);
    const uint32_t bytesToBeSent = b-genBuf;
    const int32_t mlen = fComm->SendAll(genBuf, bytesToBeSent, timeout_clock);
    const int32_t flen = fComm->FinishSending(timeout_clock);
#ifdef DEBUG
    printf("SendHndshkReq: time = %u, timeout = %u, mlen=%d, flen=%d\r\n",
        time(0), timeout_clock, mlen, flen);
#endif
    return mlen+flen;
}

SnCommWin::ECommWinResult SnCommWin::SendSignalStrength(char* const genBuf,
                                                        SnSignalStrengthFrame& sigstr,
                                                        const uint32_t timeout_clock) {
    // try to get the signal strength and send it if possible
    
    Watchdog::kick(); // don't reset
    
    float ss;
    if ( fComm->CheckSignalStrength(timeout_clock, ss) ) {
        char* b = genBuf;
        SnHeaderFrame::WriteTo(b, SnHeaderFrame::kSignalStrCode, sigstr.SizeOf());
        sigstr.SetSigStr(ss, time(0));
        sigstr.SetCommType( static_cast<uint8_t>(GetCommType()) );
        sigstr.WriteTo(b);
        const uint32_t bytesToBeSent = b - genBuf;
        const int32_t mlen = fComm->SendAll(genBuf, bytesToBeSent, timeout_clock);
        const int32_t flen = fComm->FinishSending(timeout_clock);
        if (bytesToBeSent==(mlen+flen)) {
            return SnCommWin::kOkMsgSent;
        } else {
            return SnCommWin::kFailPartSent;
        }
    }
    return SnCommWin::kFailNoneSent;
}

bool SnCommWin::WriteStatusDataPackHeaderTo(char*& b,
                                            const uint32_t payloadLen) {
    return CallHeaderWriteTo(b, 
                             SnHeaderFrame::kStatusDataPack,
                             SnHeaderFrame::SizeOf() // this is the header of the packed data, not the kStatusDataPack hdr itself
                                + payloadLen);
}

bool SnCommWin::CallHeaderWriteTo(char*& b,
                                  const uint8_t msgCode,
                                  const uint32_t msgLen) {
    // to call fro SnCommWin.h where we don't include SnHeaderFrame
    return SnHeaderFrame::WriteTo(b, msgCode, msgLen);
}

int32_t SnCommWin::CallSendAll(const char* const data,
                               const uint32_t length,
                               const uint32_t timeout_clock) {
    // to call fro SnCommWin.h where we don't include SnCommPeripheral
    if (fComm!=0) {
        return fComm->SendAll(data, length, timeout_clock);
    }
    return 0;
}

SnCommWin::ECommWinResult SnCommWin::SendStatus(const SnConfigFrame& conf,
                                                const SnPowerFrame& pow, // com win power
                                                const SnEventFrame& stEvent,
                                                const uint16_t seq,
                                                const uint32_t numThmTrigs,
                                                const uint32_t numSavedEvts, 
                                                const float    seqlive,
                                                const uint32_t powerOnTime,
                                                const SnTempFrame& temper, // com win temp
                                                char* const genBuf,
                                                const uint32_t timeout_clock) {
    // TODO: check if connected?
#ifdef DEBUG
    printf("########### Send Status\r\n");
#endif
    
    Watchdog::kick(); // don't reset

    int32_t toBeSent(0), byteSent(0), sendNow(0);
    uint8_t loseLSB=0, loseMSB=0;
    uint16_t wvBase=0;
    conf.GetPackParsFor(GetCommType(), loseLSB, loseMSB, wvBase);
    const uint32_t ssize = SnStatusFrame::SizeOf(SnStatusFrame::kIOVers, conf);
    char* b = genBuf;
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kStatusCode, ssize);
    SnStatusFrame::WriteTo(b, conf, seq,
                           numThmTrigs, numSavedEvts, seqlive,
                           powerOnTime, temper,
                           loseLSB, loseMSB, wvBase);
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kPowerCode, 
        pow.SizeOf(SnPowerFrame::kIOvers));
    pow.WriteTo(b);
    sendNow  = b-genBuf;
    toBeSent = sendNow;
#ifdef DEBUG
    printf("calling SendAll (status)\r\n");
#endif
    byteSent = fComm->SendAll(genBuf, sendNow, timeout_clock);
#ifdef DEBUG
    printf("SendAll returned %d\r\n",byteSent);
#endif
    
    //byteSent += fComm->FinishSending(timeout_clock);
#ifdef DEBUG
    printf("byteSent=%d  toBeSent=%d\r\n",byteSent,toBeSent);
    printf("status+power frame:\r\n");
    for (uint32_t i=0; i<sendNow; i++) {
        printf("%02X ",genBuf[i]);
    }
    printf("\r\n");
#endif
        
    Watchdog::kick(); // don't reset

    // finally, mark the end of the status update
    b = genBuf;
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kStatusUpdDone, 0);
    sendNow   = b-genBuf;
    toBeSent += sendNow;
#ifdef DEBUG
    printf("calling SendAll (status update done)\r\n");
#endif
    byteSent += fComm->SendAll(genBuf, sendNow, timeout_clock);


    /*
    // event compression parameters must be the same
    // as those sent in the status update
    b = genBuf;
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kEventCode,
        evt.SizeOf(SnEventFrame::kIOVers, loseLSB, loseMSB));
    b = evt.WriteTo(b, loseLSB, loseMSB, wvBase);
    msiz = b-genBuf;
#ifdef DEBUG
    printf("calling SendAll (event) %d:\r\n",msiz);
    for (uint32_t i=0; i<msiz; i++) {
        printf("%02X ",genBuf[i]);
    }
    printf("\r\n");
#endif
    mlen = fComm->SendAll(genBuf, msiz, timeout_clock);
    */
    
    // finish sending whatever didn't make it out
    byteSent += fComm->FinishSending(timeout_clock);

#ifdef DEBUG
    printf("after FinishSending\r\n");
    printf("byteSent=%u, toBeSent=%u\r\n", byteSent, toBeSent);
#endif

    Watchdog::kick(); // don't reset

    if (byteSent==toBeSent) {
#ifdef DEBUG
        printf("Sent all status bytes OK\r\n");
#endif
        return SnCommWin::kOkMsgSent;
    }
    
#ifdef DEBUG
    printf("Status failed with part-sent\r\n");
#endif
    return SnCommWin::kFailPartSent;
}
    

SnCommWin::ECommWinResult SnCommWin::SendStatusData(const SnConfigFrame& conf,
                                                    const SnConfigFrame& stConf,
                                                    const SnClockSetFrame& stTrgStartClk,
                                                    const SnClockSetFrame& stTrgStopClk,
                                                    const SnPowerFrame& stPower,
                                                    const SnEventFrame& stEvent,
                                                    const SnTempFrame& stTemperature,
                                                    const SnHeartbeatFrame& stHeartbeat,
                                                    const bool stNewPower,
                                                    const bool stNewEvent,
                                                    const bool stNewHeartbeat,
                                                    const bool stNewTemperature,
                                                    char* const genBuf,
                                                    const uint32_t timeout_clock) {
    //
    //
    // check what data we are sending with the status frame.
    //
    // ordered like they appear in SnEvts files
    //
    //
    
#ifdef DEBUG
    printf("########### Send Status Data\r\n");
#endif
    
    Watchdog::kick(); // don't reset

    int32_t toBeSent(0), byteSent(0), sendNow(0);
    char* b = genBuf;

    // power reading first
    if (conf.IsStatusSendingPowerReading() && stNewPower) {
        SendStatusDataPack(stPower,
                           SnHeaderFrame::kPowerCode,
                           toBeSent,
                           byteSent,
                           genBuf,
                           timeout_clock);
#ifdef DEBUG
        printf("Send status power data. toBeSent=%u, byteSent=%u\r\n",
            toBeSent, byteSent);
#endif
    }

    // config next
    if (conf.IsStatusSendingConfig()) {
        SendStatusDataPack(stConf,
                           SnHeaderFrame::kConfigCode,
                           toBeSent,
                           byteSent,
                           genBuf,
                           timeout_clock);
#ifdef DEBUG
        printf("Send status config. toBeSent=%u, byteSent=%u\r\n",
            toBeSent, byteSent);
#endif
    }
    
    // trigger start time next
    if (conf.IsStatusSendingTrigTimes() ) {
        SendStatusDataPack(stTrgStartClk,
                           SnHeaderFrame::kFileTrgStrtCode,
                           toBeSent,
                           byteSent,
                           genBuf,
                           timeout_clock);
#ifdef DEBUG
        printf("Send status trigger start. toBeSent=%u, byteSent=%u\r\n",
            toBeSent, byteSent);
#endif
    }
    
    // event next
    if (conf.IsStatusSendingEvent() && stNewEvent) {
        // event has a different WriteTo
        b = genBuf;
        const uint32_t evtLen = stEvent.SizeOf(SnEventFrame::kIOVers,
                                               conf.GetWvLoseLSB(),
                                               conf.GetWvLoseMSB());
        WriteStatusDataPackHeaderTo(b, evtLen);
        CallHeaderWriteTo(b, SnHeaderFrame::kEventCode, evtLen);
        b = stEvent.WriteTo(b,
                            conf.GetWvLoseLSB(),
                            conf.GetWvLoseMSB(),
                            conf.GetWvBaseline());
        sendNow   = (b - genBuf);
        toBeSent += sendNow;
        byteSent += CallSendAll(genBuf, sendNow, timeout_clock);

#ifdef DEBUG
        printf("Send status event. toBeSent=%u, byteSent=%u\r\n",
            toBeSent, byteSent);
#endif
    }
    
    // temperature next
    if (conf.IsStatusSendingTemperature() && stNewTemperature) {
        SendStatusDataPack(stTemperature,
                           SnHeaderFrame::kTemperatureCode,
                           toBeSent,
                           byteSent,
                           genBuf,
                           timeout_clock);
#ifdef DEBUG
        printf("Send status temperature. toBeSent=%u, byteSent=%u\r\n",
            toBeSent, byteSent);
#endif
    }

    // heartbeat next
    if (conf.IsStatusSendingHeartbeat() && stNewHeartbeat) {
        SendStatusDataPack(stHeartbeat,
                           SnHeaderFrame::kHeartbeatCode,
                           toBeSent,
                           byteSent,
                           genBuf,
                           timeout_clock);
#ifdef DEBUG
        printf("Send status temperature. toBeSent=%u, byteSent=%u\r\n",
            toBeSent, byteSent);
#endif
    }

    // trigger stop time next
    if (conf.IsStatusSendingTrigTimes() ) {
        SendStatusDataPack(stTrgStopClk,
                           SnHeaderFrame::kFileTrgStopCode,
                           toBeSent,
                           byteSent,
                           genBuf,
                           timeout_clock);
#ifdef DEBUG
        printf("Send status trigger stop. toBeSent=%u, byteSent=%u\r\n",
            toBeSent, byteSent);
#endif
    }

    // finally, mark the end of the status update
    if (toBeSent>0) {
        b = genBuf;
        SnHeaderFrame::WriteTo(b, SnHeaderFrame::kStatusDatPakDone, 0);
        sendNow   = b-genBuf;
        toBeSent += sendNow;
#ifdef DEBUG
        printf("calling SendAll (status pak done)\r\n");
        printf("timed out = %s\r\n", (fComm->IsTimedOut(timeout_clock) ? "true" : "false"));
        printf("time(0)=%u, timeout_clock=%u\r\n", time(0), timeout_clock);
#endif
        byteSent += fComm->SendAll(genBuf, sendNow, timeout_clock);
        
#ifdef DEBUG
        printf("calling FinishSending (status pak done)\r\n");
#endif
        // finish sending whatever didn't make it out
        byteSent += fComm->FinishSending(timeout_clock);

#ifdef DEBUG
        printf("after FinishSending status data\r\n");
#endif
    } // if toBeSent>0

#ifdef DEBUG
    printf("byteSent=%u, toBeSent=%u\r\n", byteSent, toBeSent);
    printf("timed out = %s\r\n", (fComm->IsTimedOut(timeout_clock) ? "true" : "false"));
    printf("time(0)=%u, timeout_clock=%u\r\n", time(0), timeout_clock);
#endif

    if (byteSent==toBeSent) {
#ifdef DEBUG
        printf("Sent all status data bytes OK\r\n");
#endif
        return SnCommWin::kOkMsgSent;
    }
    
#ifdef DEBUG
    printf("Status data failed with part-sent\r\n");
#endif
    return SnCommWin::kFailPartSent;
}


SnCommWin::ECommWinResult SnCommWin::SendString(const char* str,
                                                const uint32_t timeout) {
#ifdef DEBUG
    printf("SnCommWin::SendString %s\r\n",str);
#endif
    Watchdog::kick(); // don't reset

    const bool ok = fComm->SendString(str, timeout);
    return (ok) ? SnCommWin::kOkMsgSent : SnCommWin::kFailPartSent;
}

int32_t SnCommWin::SendFilename(const char* inf,
                                char* const genBuf,
                                int32_t&    bytesToBeSent,
                                const uint32_t timeout_clock) {
#ifdef DEBUG
    printf("SnCommWin::SendFilename %s\r\n",inf);
#endif
    
    Watchdog::kick(); // don't reset
    
    // remove the directory
    const char* fn = strrchr(inf, '/');
    if (fn==0) {
        // no directory
        fn = inf;
    } else if (fn!=inf) {
        ++fn; // move past the '/' if it was found
    }
#ifdef DEBUG
    printf("send %s\r\n",fn);
#endif
    const size_t flen = strlen(fn);
    char* b = genBuf;
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kFilenameCode, flen);
    b = SnBitUtils::WriteTo(b, fn, flen);
    bytesToBeSent = b-genBuf;
    const int32_t mlen = fComm->SendAll(genBuf, bytesToBeSent, timeout_clock);
#ifdef DEBUG
    printf("time = %u, timeout = %u\r\n",time(0), timeout_clock);
#endif
    return mlen;
}

int32_t SnCommWin::SendFileBlock(FILE* inf, 
                                 const uint8_t blockHeaderCode,
                                 const uint32_t blockSize,
                                 char* const genBuf,
                                 int32_t& bytesToSend,
                                 const uint32_t timeout) {
    // sends the block from the file. does not check if the file has sufficient bytes
    // this should be done before calling this function
    // (where the number of bytes in the file can be cached)
    //
    // bytesToSend will be set to the total bytes that should be delivered eventually
    // the number of bytes actually shipped out by SendAll is returned
    
    Watchdog::kick(); // don't reset
    
    char* b = genBuf;
    SnHeaderFrame::WriteTo(b, blockHeaderCode, blockSize);
    SnBitUtils::ReadFrom(inf, b, blockSize);
#ifdef DEBUG
    printf("Sending block hc %02x, len=%u\r\n",blockHeaderCode,blockSize+SnHeaderFrame::SizeOf());
#endif
    bytesToSend = blockSize+SnHeaderFrame::SizeOf();
    return fComm->SendAll(genBuf, bytesToSend, timeout);
}                             

int32_t SnCommWin::SendFileContents(FILE* inf,
                                    const SnConfigFrame& curConf,
                                    SnEventFrame& evt,
                                    SnPowerFrame& pow,
                                    char* const genBuf,
                                    uint32_t nevts,
                                    int32_t& bytesToBeSent,
                                    const uint32_t timeout_clock) {
    // firstEvt==0 ==> start at beginning
    // nevts==0 ==> all events
    //
    // bytesToSend will be set to the total bytes that should be delivered eventually
    // the number of bytes actually shipped out by SendAll is returned
    
#ifdef DEBUG
    printf("SendFileContents (byte streaming)\r\n");
#endif
    
    Watchdog::kick(); // don't reset
    
    // store position in file so we can go back to it afterwards
    // this is probably not necessary, because if this file is open
    // in write-only mode, we're screwed..
    const int fpos = ftell(inf);
    if (fpos>0) {
        fseek(inf, 0, SEEK_SET);
    }
    // how many bytes?
    fseek(inf, 0, SEEK_END); // go to end
    const int fend = ftell(inf);
    fseek(inf, 0, SEEK_SET); // go to start
#ifdef DEBUG
    printf("fend=%d\r\n",fend);
    printf("fpos=%d\r\n",fpos);
#endif
    
    // variables used when sending data
    char* b = genBuf;
    int32_t msiz(0), mlen(0), mtogo(0), tsent(0);
    // count number of events / power readings sent
    uint32_t evtsSent=0, powsSent=0, crc=0;
    
    Watchdog::kick(); // don't reset
    
    // first is the file header, which has no SnHeaderFrame
    msiz = SnSDUtils::SizeOfFileHeader(SnSDUtils::kIOvers);
    bool ok = (ftell(inf)+msiz)<=fend;
    if (ok) {
        mlen = SendFileBlock(inf, SnHeaderFrame::kFileHeadrCode, 
                             msiz, genBuf, mtogo, timeout_clock);
        //ok &= (msiz+SnHeaderFrame::SizeOf())==mlen;
        bytesToBeSent += mtogo;
        tsent += mlen;
#ifdef DEBUG
        printf("sent file header. ok=%d\r\n",(int)ok);
#endif
        
        Watchdog::kick(); // don't reset
        
        // calc crc w/o the header block of the file header, as
        // this does not get stored in the file
        crc = SnCRCUtils::GetUpdatedCRC32for(crc, genBuf+SnHeaderFrame::SizeOf(),
                                             mtogo-SnHeaderFrame::SizeOf());

        // the header info for each block
        uint8_t hcode(0);
        uint32_t hlen(0);
        
        // now just loop through file and send each block
        bool fok = true;
        while (    fok
                && (feof(inf)==0)
                && (ferror(inf)==0)
                && ((ftell(inf)+SnHeaderFrame::SizeOf())<=fend) ) {
            
            Watchdog::kick(); // don't reset
            
            // read the header for the block
            SnSDUtils::ReadBlockHeader(inf, hcode, hlen);
            fok &= (ftell(inf)+hlen)<=fend;
#ifdef DEBUG
            printf("new block: hc=%02x, hl=%u, ftell=%d, fend=%d, "
                "ok=%d, fok=%d\r\n",
                hcode, hlen, ftell(inf), fend, (int)ok, (int)fok);
#endif
            if (fok) {
                // send the block
                // TODO: repack events?
                mlen = SendFileBlock(inf, hcode, hlen, genBuf, 
                                     mtogo, timeout_clock);
                bytesToBeSent += mtogo;
                tsent += mlen;
                crc = SnCRCUtils::GetUpdatedCRC32for(crc, genBuf, mtogo);

                if (hcode==SnHeaderFrame::kEventCode) {
                    ++evtsSent;
                    if (nevts>0) {
                        if (evtsSent>=nevts) {
#ifdef DEBUG
                            printf("sent %u events. stop\r\n",evtsSent);
#endif
                            break;
                        }
                    }
                } else if (hcode==SnHeaderFrame::kPowerCode) {
                    ++powsSent;
                }
            } // otherwise file size not sufficient for this block
        } // loop over file contents
    } else {
        // otherwise file size not sufficient for file header
        // so it's either 0 or corrupted.
        // proceed as normal even tho no contents were sent.
        // if we're deleting files, this one will be deleted.
        ok = true;
    }        
#ifdef DEBUG
    printf("loop done. ok=%d\r\n",(int)ok);
#endif

    Watchdog::kick(); // don't reset

    // send the crc
#ifdef DEBUG
    printf("sending crc (%u)\r\n",crc);
#endif
    b = genBuf;
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kMbedFileChksCode, sizeof(uint32_t));
    b = SnBitUtils::WriteTo(b, crc);
    msiz = b - genBuf;
    mlen = fComm->SendAll(genBuf, msiz, timeout_clock);
    bytesToBeSent += msiz;
    tsent += mlen;
    
#ifdef DEBUG
    printf("(msiz=%d, mlen=%d)\r\n", msiz, mlen);
#endif
    
    Watchdog::kick(); // don't reset
    
    // send number of events sent
#ifdef DEBUG
    printf("sending evtsSent (%u)\r\n",evtsSent);
#endif
    b = genBuf;
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kFileNevtsCode, sizeof(uint32_t));
    b = SnBitUtils::WriteTo(b, evtsSent);
    msiz = b - genBuf;
    mlen = fComm->SendAll(genBuf, msiz, timeout_clock);
    bytesToBeSent += msiz;
    tsent += mlen;
    
#ifdef DEBUG
    printf("(msiz=%d, mlen=%d)\r\n", msiz, mlen);
#endif
    
    Watchdog::kick(); // don't reset
    
    // send number of power readings sent
#ifdef DEBUG
    printf("sending powsSent (%u)\r\n",powsSent);
#endif
    b = genBuf;
    SnHeaderFrame::WriteTo(b, SnHeaderFrame::kFileNpwrsCode, sizeof(uint32_t));
    b = SnBitUtils::WriteTo(b, powsSent);
    msiz = b - genBuf;
    mlen = fComm->SendAll(genBuf, msiz, timeout_clock);
    bytesToBeSent += msiz;
    tsent += mlen;
    
#ifdef DEBUG
    printf("(msiz=%d, mlen=%d)\r\n", msiz, mlen);
#endif

    // put file position back
    fseek(inf, fpos, SEEK_SET);
    
#ifdef DEBUG
    printf("end contents: ok=%d\r\n",(int)ok);
#endif
    
    return tsent;
    
    //return ok ? SnCommWin::kOkMsgSent : SnCommWin::kFailPartSent;
}