program to test the TCP socket connection of EthernetInterface. specifically, to reproduce a problem that results in the mbed re-sending the wrong packet after a packet loss

Dependencies:   EthernetInterface FatFileSystemCpp SDFileSystem mbed-rtos mbed

main.cpp

Committer:
uci1
Date:
2012-09-22
Revision:
1:6b90cbfe0c9e
Parent:
0:53afc51b78a9

File content as of revision 1:6b90cbfe0c9e:

#include "mbed.h"

#include "SDFileSystem.h"
#include "TCPSocketConnection.h"
#include "EthernetInterface.h"
#include "string"
#include "fakeData.h"

//#define DEBUG                 // turn on debug output if defined (note this slows down processing due to serial baud rate!)
//#define PRINT_DOIO_RESULT     // print the number of bytes DoIO returns

// if this is defined, read files starting with "S" from the SD card
// otherwise use the fake data in fakeData.h (from memory)
//#define USE_DATA_FROM_SD

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

static const char*          kRserv      = "128.195.204.151";
static const unsigned short kRport      = 6666;

static const char*          kMyIp       = "128.195.204.148";
static const char*          kMyMask     = "255.255.255.0";
static const char*          kMyGate     = "128.195.204.1";

static const unsigned int   kBSTime     = 946684800u; // 1/1/2000 00:00:00 UTC
static const int            kSecsPerDay = 3600*24;
static const int            kBuffSize   = 1024;
// all chunks must be smaller than kBuffSize
static const int            kNCsizes    = 5;
static const int            gChunkSize[kNCsizes] = { 9, 26, 711, 40, 432 };
static const int            kTotFakeDat = 2097152; // if sending fake data, send 2 MB total
static const int            kTimeout    = 300; // seconds

static const char*          kSDsubDir   = "/sd";
static const char*          kFileTag    = "S"; // send files starting with this tag

static char                 gBuff[kBuffSize];
static SDFileSystem         sd(p5, p6, p7, p8, kSDsubDir+1);
static LocalFileSystem      local("local");
static EthernetInterface*   gEth(0);
static TCPSocketConnection* gSock(0);
static Serial               gCpu( USBTX, USBRX );

// function pointer to send, receive, etc
typedef int (TCPSocketConnection::*TCPSendRecv)(char*, int);
#define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))

// forward declarations
bool Connect(const int timeout);
void SendData(FILE* inf, const int timeout);
FILE* OpenSDFile(const char* name, const char* mode);
void SendAllFakeData(const int totalBytes, const int timeout);
void SendAllFiles(const int timeout);
bool IsTimedOut(const int timeout_clock);
void dispStrBytes(const char* const s, const int len);
int DoIO(char* const data,
         const int length,
         const int timeout_clock,
         TCPSendRecv fcn);
int ReceiveAll(char* const buf, const int mlen, const int timeout_clock);
int SendAll(char* const data, const int length, const int timeout_clock);


int main() {
    led1=1;
    set_time(kBSTime);
    if (Connect(time(0)+kTimeout)) {
        led2=1;
#ifdef USE_DATA_FROM_SD
        SendAllFiles(time(0)+kTimeout);
#else
        SendAllFakeData(kTotFakeDat, time(0)+kTimeout);
#endif
    } else {
        led4=1;
    }
    if (gSock!=0) {
        gSock->close();
    }
    if (gEth!=0) {
        gEth->disconnect();
    }
    while (true) {
        led1=0; wait(0.5); led1=1; wait(0.5);
    }
}

bool Connect(const int timeout) {

#ifdef DEBUG
    printf("Connect\r\n");
#endif
    
    gEth = new EthernetInterface;
    gSock = new TCPSocketConnection;

    gEth->init(kMyIp, kMyMask, kMyGate);

    bool isConn = false;

    while ( (isConn==false) && (IsTimedOut(timeout)==false) ) {
        wait_ms(250);
        isConn = (gEth->connect()==0);
    }
#ifdef DEBUG
    printf("eth isConn=%d\r\n",(int)isConn);
#endif
    
    isConn=false;
    while ( (isConn==false) && (IsTimedOut(timeout)==false) ) {
        wait_ms(250);
        isConn = (gSock->connect(kRserv, kRport)==0);
    }
#ifdef DEBUG
    printf("sock isConn=%d\r\n",(int)isConn);
#endif
    return isConn;
}

void SendAllFakeData(const int totalBytes, const int timeout) {
    // send the fake data in chunks

    const int* const csend = gChunkSize + kNCsizes;
    const int* cs = gChunkSize;
    
    int br=0;  // bytes read/sent so far
    int tcs=0; // this chunk size
    while ( br<totalBytes ) {
        
        // read a chunk
        tcs = (*cs < kFakeDataSize) ? (*cs) : kFakeDataSize;
        memcpy(gBuff, gFakeData, tcs);
        
        // send this chunk
        br += SendAll(gBuff, tcs, timeout);
        
        // change chunk size
        ++cs;
        if (cs==csend) {
            cs = gChunkSize;
        }
    }
}

void SendData(FILE* inf, const int timeout) {
    // send the SD card data in chunks
    
    // store position in file so we can go back to it afterwards
    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
    
    const int* const csend = gChunkSize + kNCsizes;
    const int* cs = gChunkSize;
    
    int br=0;  // bytes read/sent so far
    int tcs=0; // this chunk size
    while ( br<fend ) {
        
        // read a chunk or the remainder of the file
        tcs = ((br+(*cs))<fend) ? (*cs) : (fend-br);
        fread(gBuff, tcs, 1, inf);
        
        // send this chunk
        br += SendAll(gBuff, tcs, timeout);
        
        // change chunk size
        ++cs;
        if (cs==csend) {
            cs = gChunkSize;
        }
    }
    
}

FILE* 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
    return fopen(fn.c_str(), mode);
}

void SendAllFiles(const int timeout) {

    DIR* d;
    struct dirent* dent;
    
    if ( (d = opendir( kSDsubDir ))!=NULL ) {
        FILE* f;
        while ( (dent = readdir(d))!=NULL ) {
            if (strncmp(dent->d_name, kFileTag, strlen(kFileTag))==0) {
                f = OpenSDFile(dent->d_name, "rb");
                SendAll(dent->d_name, strlen(dent->d_name), timeout);
                SendData(f, timeout);
                fclose(f);
            }
        }
        closedir(d);
    }
}

bool IsTimedOut(const int timeout_clock) {
    if (timeout_clock==0) {
        // for not obeying timeout option
        return false;
    } else {
        const int ct = time(0);
        if ( (ct==0) ||
             (abs(static_cast<double>(timeout_clock-ct))>kSecsPerDay) ) {
            // clock problems! timeout now.
#ifdef DEBUG
            printf("clock problem. timing out\r\n");
#endif            
            return true;
        } else {
            const bool rt = (ct>timeout_clock);
#ifdef DEBUG
            if (rt) {
                printf("timing out.\r\n");
            }
#endif
            return rt;
        }
    }
}

void dispStrBytes(const char* const s, const int len) {
    const char* c = s;
    for (int i=0; i<len; ++i, ++c) {
        if (*c>0x1F && *c<0x7F) {
            printf("%c", *c);
        } else {
            printf(".x%02x.", *c);
        }
    }
}

int DoIO(char* const data,
         const int length,
         const int timeout_clock,
         TCPSendRecv fcn) {
    // TODO: if B64, must return number of bytes of raw (non encoded) message
#ifdef DEBUG
    printf("DoIO data:\r\n");
    dispStrBytes(data, length);
    printf("\r\n");
#endif
    int res=0;
    int b=0;
    while ( (length>b) ) {
        if (IsTimedOut(timeout_clock)) {
#ifdef DEBUG
            printf("DoIO timing out\r\n");
#endif
            break;
        }
        res = CALL_MEMBER_FN(*gSock, fcn)(data+b, length-b);
        switch (res) {
            case -1:
                // TODO: how to check the error?
                continue;
            case 0:
                return b;
            default:
                b += res;
        };
        //wait_ms(100);
    }
#if defined(DEBUG) || defined(PRINT_DOIO_RESULT)
    printf("return b=%d\r\n",b);
#endif
    return b; // timeout
}

int ReceiveAll(char* const buf, const int mlen, const int timeout_clock) {
    // use regular receive; DoIO will do a receive_all but use our timeout
    return DoIO(buf, mlen, timeout_clock, &TCPSocketConnection::receive);
}

int SendAll(char* const data, const int length, const int timeout_clock) {
    // use regular send; DoIO will do a send_all but use our timeout
    return DoIO(data, length, timeout_clock, &TCPSocketConnection::send);
}