The field version of the solarnano grid on the ionQubes

Fork of SolarNanoGridv3 by SONG Project

Battery/Battery.cpp

Committer:
defrost
Date:
2016-06-23
Revision:
29:bbdbe1094a63
Parent:
28:e85a86fc8b59
Child:
30:47f73b850a5e

File content as of revision 29:bbdbe1094a63:

/**
 *@section DESCRIPTION
 * mbed SolarNanogrid  Library
 * Battery extends SolarNanoGrid.
 * Battery interacts with the lockers.
 * The ID must NOT end in 00.
 *@section LICENSE
 * Copyright (c) 2016, Malcolm McCulloch
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * @file "Battery.c"
 */
#include "Battery.h"
#define FUNCNAME "BATTERY"
#include "defs.h"

// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------

Battery::Battery(FILE* fp, Serial *pc) : SolarNanoGrid(fp,pc)
{
    DBG("Initialize class");
    // ***** Variable initialisation *****
    // Public variable initialisation

    maxChargeRate=1.0; // Charge rate in amps

    // Protected variables initialisation
    count=0;
    sizeRead=0;
    fileLength=0;
    filePointer=0;
    fileLeft=0;

    sdBuffPnt=0;
    /**
     * Open channel address of the locker
     */
    openAddr=0;

    /**
     * Private channel address of the locker
     */
    privateAddr=0;

    countSD=0;

    // Protected flags: *
    checkedIn=0;
    flagDirFileName=0;
    flagGetTime=0;
    flagNextFile=0;
    flagSendFileName=0;
    flagSendFileSize=0;
    flagSendFile=0;
    flagSendFileDone=0;
    flagGetMaxCharge=0;
    sendingFile=0;
    flagStartFileTx=0;
    startCharge = false;
    flagHubNotReady=0;
    flagCheckOut=0;
    sending=0;
    // Protected interrupts
    button=NULL;
    txWatch=NULL;
    delay=NULL;

    //Private variables initialisation
    maxRT=false;

    // ***** End of  variables initialisation *****

    // No other information needed from the config file
    fclose(fp);
    //
    // Setup variables
    //
    txFileName = (char*) calloc(80, 1);
    fullTxFilePath = (char*) calloc(80, 1);
    logDir = (char*) calloc(20, 1);
    sdBuffer = (char*) calloc(SDBUFFERSIZE, 1);




    //
    // Setup NRF
    //
    privateAddr = ((long long)communityID <<16)+ (id &0XFFFF);
    openAddr = privateAddr & 0xFFFFFFFF00;

    DBG("Channel %x  Address %llx Address Locker %llx", chan, privateAddr, openAddr);

    spiNRF();
    nrf->quickTxSetup(chan, openAddr);
    nrf->setRadio(0x01, 0x03); // 2MB/S  0dB
    nrf->setTxRetry(0x0F, 0x0F);
    nrfFlush();
    DBG("Nrf Details:");
#ifdef DEBUG
    nrf->printDetails();
#endif
    //
    // Setup logging directories
    //
    sprintf(logDir, "/sd/data/%02X%04X", (communityID&0XFF),(id&0XFFFF));
    DBG("  Log directory %s", logDir);
    spiSD();
    mkdir("/sd/data", 777);
    mkdir(logDir, 777);
    DBG("    made");
    //
    // Setup interrupts
    //
    button = new InterruptIn(SW2);
    button->fall(this, &Battery::intButton);

    nrfInt = new InterruptIn(PTC18);
    nrfInt->fall(this, &Battery::intNrf); // attach nrf interrupt.

    txWatch = new Ticker ();
    txWatch->attach(this,&Battery::retransmit,10.01);

    delay = new Timeout();

    loop();
}

// ------------------------------------------------------------------------
// Interrupt routines
// ------------------------------------------------------------------------

/**
 * When the button is pressed transmit something
 */
void Battery::intButton()
{
    if (!maxRT) {
        *ce = 1;
        if(checkedIn==0) {
            checkIn();
        } else {
            flagNextFile = 1;
        }

    }
}

/**
 * Called when the nrf creates an interrupt.
 *
 */
void Battery::intNrf()
{
    //  DBG("NRF Interrupt Started.");
    int pipe=0;
    int status=0;
    int width=0;

    //INFO("intNrf");

    //
    //Get status and pipe
    //
    spiNRF();
    status = nrf->checkStatus();
    pipe = (status >> 1);
    pipe = (pipe & 0x0007);

    //
    // Check if max RT is reached
    //
    /*if (((status >>MAX_RT)& 01) ==1) {
        if (flagStartFileTx==1) { // Locker not ready to reacieve - wait 25s.
            flagHubNotReady = 1;
            nrf->clearStatus();
            flag = &flagDirFileName;
            delay->detach();
            delay->attach(this, &Battery::setFlag, 25.0f);
        } else {
            maxRT=true;
            DBG("MAX RT flag is set. PTB20= %d status=%x",(unsigned int) *ce,status);
            *ce = 0;
            DBG("PTB20= %d status=%x",(unsigned int) *ce,status);
            nrf->clearStatus();
            *ledRed=0;
        }
    }*/

    //
    // Check if data received
    //
    if (((status >>RX_DR)& 01) ==1) { // Received a packet
        width= nrf->getRxData(dataRx);
        dataRx[width]='\0';
        if (dataRx[0]!='S') {
            DBG("intNrf>D[p=%1d]=<%2s> ",pipe,dataRx);
        }
        maxRT=false;
        flagHubNotReady = 0;
        *ce = 1;
        nrf->clearStatus();
        switch (dataRx[0]) {
            case ('C'): {
                doCheckIn();
                flagNextFile = 1;
                DBG("Flag Set");
                break;
            }
            case ('D'): {
                flagStartFileTx=0; // Hub commmunicating.
                flag = &flagSendFileName;
                delay->detach();
                delay->attach(this, &Battery::setFlag, 1.5);
                break;
            }
            case ('F'): {
                flag = &flagSendFileSize;
                countSD = 0;
                delay->detach();
                delay->attach(this, &Battery::setFlag, 1.50);
                break;
            }
            case ('O'): {
                doCheckOut();
                break;
            }
            case ('S'): {
                flag = &flagSendFile;

                delay->detach();
                //  printf("%d\n\r",countSD%16);
                //delay->attach(this, &Battery::setFlag, 0.05);

               if (countSD % (SDBUFFERSIZE/32) == 0) {
                    delay->attach(this, &Battery::setFlag, 1.5); // wait to write to SD card
                } else {
                    delay->attach_us(this, &Battery::setFlag, 800); // was 800
                }
                countSD++;
                break;
            }
            case ('T'): {
                time_t now;
                sscanf (&dataRx[2],"%x",&now );
                set_time(now);
                now= time(NULL);
                struct tm * timeInf = localtime(&now);
                DBG ("Time is now: %04d-%02d-%02d %02d:%02d:%02d",timeInf->tm_year+1900,timeInf->tm_mon+1,timeInf->tm_mday,timeInf->tm_hour, timeInf->tm_min,timeInf->tm_sec);

                break;
            }


            case ('M'): {

                sscanf (&dataRx[2],"%f",&maxChargeRate );
                if(maxChargeRate>2.0f) {
                    maxChargeRate=2.0f;
                }
                if (maxChargeRate<0.0f) {
                    maxChargeRate=0.0f;
                }
                DBG ("Max charge %f",maxChargeRate);

                break;
            }
        }

    }

    if (((status >>TX_DS)& 01) ==1) {
        //  maxRT=false;
        nrf->flushTx();
        nrf->flushRx();
        //DBG(" TX_DS");
    }

    *ledBlue = !*ledBlue;
    *ledRed = !maxRT;
}

/**
 * Transmits the data and sets up a call back to check data sent 30s later.
 */

/**
 * Checks to see if transmit failed and then retransmit
 *
 */
void Battery::retransmit()
{

    if (maxRT) {
        DBG("Retransmit");
        *ledRed=1;
        *ce = 1;
        maxRT=false;
        nrf->clearStatus();
        nrf->flushRx();
        nrf->retransmitData();
    }
    if ((!sendingFile)&&(checkedIn == 1)) {
        flagGetTime=1;
        flagGetMaxCharge=1;
    }
}

// ------------------------------------------------------------------------
// Main routines
// ------------------------------------------------------------------------

/**
 * Checks in the Battery to the locker
 * Transmits "C ID"
 *
 */
void Battery::checkIn()
{
    sprintf(dataTx, "C %04X", (int) (id&0XFFFF));
    DBG("Check into locker [%s]", dataTx);
    spiNRF();
    nrf->transmitData(dataTx, strlen(dataTx));
}


/**
* Checks out. Sends O %d, ID to locker
*
*/
void Battery::checkOut()
{
    sprintf(dataTx, "O %04X", (int) (id&0XFFFF));
    DBG("Check out locker [%s]", dataTx);
    spiNRF();
    nrf->transmitData(dataTx, strlen(dataTx));
    // Reset to locker public comms channel
}


/**
 * Checks in the Battery to the locker
 * Acknowledge received.
 * Changes the TX pipe address.
 *
 */
void Battery::doCheckIn()
{
    nrf->setTxAddress(privateAddr);
    checkedIn = 1;
    DBG("Checked into locker");
    DBG("Nrf Details:");
#ifdef DEBUG
    nrf->printDetails();
#endif
}
/**
 * Checks in the Battery to the locker
 * Acknowledge received.
 * Changes the TX pipe address.
 *
 */
void Battery::doCheckOut()
{
    nrf->setTxAddress(openAddr);
    checkedIn = 0;
    DBG("Checked out of locker");
    DBG("Nrf Details:");
#ifdef DEBUG
    nrf->printDetails();
#endif
}

/**
 * Slow routines for Battery
 */
void Battery::loop()
{
    //INFO("In Battery Loop");
    *ledGreen = !checkedIn;


    if (flagGetTime) {
        flagGetTime=0;
        requestTime();

    }
    if (flagGetMaxCharge) {
        flagGetMaxCharge=0;
        requestMaxCharge();

    }
    if (flagNextFile) {
        flagNextFile = 0;
        sending=1;
        DBG("NextFile called");
        requestTime();
        nextFileToTx();
        if (fileLength>0) {
            INFO("Sending file %s.",txFileName)
            flagDirFileName=1;
        } else {
            sendingFile=0;
            flagStartFileTx=0;
            INFO("No more files to send.")
            startCharge = true;
            checkOut();
            sending=0;
        }
    }
    if (flagDirFileName) {
        flagDirFileName=0;
        sendDirName();
    }
    if (flagSendFileName) {
        flagSendFileName = 0;
        sendFileName();

    }
    if (flagSendFileSize) {
        flagSendFileSize = 0;
        sendFileSize();
    }
    if (flagSendFile) {
        flagSendFile = 0;
        sending=1;
        sendFile();
    }
    if (flagSendFileDone) {
        flagSendFileDone = 0;
        sendFileDone();
        INFO("File sent <%s>",txFileName);
        flag = &flagNextFile;
        delay->detach();
        delay->attach(this, &Battery::setFlag, 2.5);
    }
    if (flagCheckOut) {
        flagCheckOut=0;
        doCheckOut();
    }
    //INFO("Out Battery Loop");
    return;
}

/**
 * sends a request for time update.
 */
void Battery::requestTime()
{
    if(sending==0) {
        DBG("getTime");
        nrf->transmitData("T ",2);
    }
}

/**
 * sends a request for time update.
 */
void Battery::requestMaxCharge()
{
    if(sending==0) {
        DBG("Get Max Charge Rate");
        nrf->transmitData("M ",2);
    }
}
/**
 * Selects the next file to transmit
 * Uses /sd/lastFileTx.txt to store the lst file transmitted
 */
void Battery::nextFileToTx()
{

    DBG("> nextTxFile");

    char lastFileName[64];
    char fileName[64];
    char fn[40];
    unsigned int txLength;

    sdBuffPnt=0;// Tells sendFile to read from SD the first time.
    fileLength = 0;
    strcpy(txFileName, "");
    spiSD();
    // Read in last file name
    FILE *fp = fopen("/sd/lastFileTx.txt", "r");
    if (fp != NULL) {
        if (fscanf(fp, "%s %u", lastFileName, &txLength) != 2) {
            sprintf(lastFileName, "0");
        }
        fclose(fp);
    } else {
        sprintf(lastFileName, "0");
        txLength = 0;
    }
    DBG("nextFileToTx>Last Filename=%s len=%u",lastFileName,txLength);
    // Open directory and read files
    DIR *dp;
    struct dirent *dirp;
    spiSD();
    dp = opendir(logDir);
    if (dp == NULL) {
        DBG("nextTxFile logdir %s is NULL", logDir);
    } else
        //read all directory and file names in current directory into filename vector
        while ((dirp = readdir(dp)) != NULL) {
            DBG("nextFileToTx> WHILE lst [%s]  dir [%s] %d",lastFileName, dirp->d_name,strcmp(lastFileName, dirp->d_name));

            if (strcmp(lastFileName, dirp->d_name) < 0) {
                strcpy(fileName, dirp->d_name);
                sprintf(txFileName, "%s/%s", logDir, dirp->d_name);
                fp = fopen(txFileName, "r");
                fseek(fp, 0L, SEEK_END);
                fileLength = ftell(fp);
                fclose(fp);
                DBG("CHOSEN %s old %s ", fileName, lastFileName );
                break;
            }
            if (strcmp(lastFileName, dirp->d_name) == 0) {

                strcpy(fileName, dirp->d_name);
                sprintf(fn, "%s/%s", logDir, dirp->d_name);
                fp = fopen(fn, "r");
                fseek(fp, 0L, SEEK_END);
                fileLength = ftell(fp);
                fclose(fp);
                if (txLength < fileLength) {
                    sprintf(txFileName, "%s/%s", logDir, dirp->d_name);
                    DBG("More to send is %s old %u new %u ", fileName, txLength,fileLength );
                    break;
                }
            }
            // test filename and if greater than last read then choose.
        }
    closedir(dp);
    if (strlen(txFileName) > 0) {
        strcpy(txFileName, fileName);
        sprintf(fullTxFilePath, "%s/%s", logDir, txFileName);
        fileLeft = fileLength;

    } else {
        fileLength =0;
    }
    DBG("nextTxFile> Next is [%s] length %d", txFileName, fileLength);

}

/**
 * Sends the directory name, allows the otherside to create.
 */

void Battery::sendDirName()
{
    flagStartFileTx=1;
    sprintf(dataTx, "D %s", logDir);

    DBG("send Dir Name [%s].", dataTx);

    spiNRF();
    nrf->transmitData(dataTx, strlen(dataTx));

}
/**
 * Sends the file name, if fileLength >0
 */

void Battery::sendFileName()
{
    // delay->detach();
    if (fileLength < 1) {
        return;
    }
    sprintf(dataTx, "F %20s", txFileName);

    DBG("send File Name [%s] \n\r", dataTx);

    spiNRF();
    nrf->transmitData(dataTx, strlen(dataTx));
}

/**
 * Sends the file length to the locker - this is also the cue that the following bytes are the file.
 */
void Battery::sendFileSize()
{
    if (fileLength < 1) {
        return;
    }
    sendingFile=1;
    filePointer = 0;
    sprintf(dataTx, "S %X", fileLength);


    DBG("send File size [%s]", dataTx);

    spiNRF();
    nrf->transmitData(dataTx, strlen(dataTx));
}

/**
 * Sends the file length to the locker - this is also the cue that the following bytes are the file.
 */
void Battery::sendFile()
{

    //  printf("send File [%s] at %d  %d\n\r",fullTxFilePath,fileLength, fileLeft );

    //  if (fileLength < 1) {
    //      INFO("File Sent");
    //      sdBuffPnt=0;
    //      return;
    //  }
    if (sdBuffPnt>=SDBUFFERSIZE) {
        sdBuffPnt=0;
    }
    
    if (sdBuffPnt == 0) {
        spiSD();
        FILE *fp = fopen(fullTxFilePath, "rb");
        if (fp == NULL) {
            spiNRF();
            INFO("sendFile> File %s not found for reading",fullTxFilePath);
            return;
        }
        spiSD();
        INFO("FileLength: %u, fileLeft: %u, SD Buffer Size: %u, Full file name: %s", fileLength, fileLeft, SDBUFFERSIZE, fullTxFilePath);
        fseek(fp, fileLength - fileLeft, SEEK_SET);
        spiSD();
        sizeRead = fread(sdBuffer, 1,SDBUFFERSIZE, fp);
        fileLeft -= sizeRead;
        fclose(fp);
        //      printf("File %s %d \n\r",fullTxFilePath,sizeRead);
        spiNRF();

    }
    spiNRF();
    
    if ((count++) % 256 ==0) {
        DBG("W %d %d", fileLeft, sizeRead);
    }
    int size=32;
    if (sizeRead<32) {
        size=sizeRead;
    }
    if ((fileLeft==0)&&(sizeRead==0)) {
        sdBuffPnt=0;
        flagSendFileDone=1;
        DBG("Done sending");
    } else {
        
        sizeRead -= size;
        spiNRF();
        nrf->transmitData(&(sdBuffer[sdBuffPnt]), size);
        sdBuffPnt+=size;
    }
    

}

/**
 * Update the last file sent file
 */
void Battery::sendFileDone()
{
    DBG("Send File Done. Writing to lastFileTx.txt...");
    if (strlen(txFileName)>0) {
        spiSD();
        FILE *fp = fopen("/sd/lastFileTx.txt", "w");
        if (fp != NULL) {
            spiSD();
            fprintf(fp, "%s %u \n", txFileName, fileLength);
            spiSD();
            fclose(fp);
            DBG("sendFileDone> updated lastFileTx.txt %s %u ", txFileName, fileLength);
        } else {
            INFO("sendFileDone> Cannot update file lastFiletx.txt");
        }
        spiNRF();

    }

}

/**
 * Sets the value pointed to by flag to 1.
 */
void Battery::setFlag()
{
    if (flag != NULL) {
        *flag = 1;
    }
    flag = NULL;
}


/**
 * Returns the max current that can be drawn from the system, in amps
 */

float Battery::getMaxCurrent()
{
    return maxChargeRate;
}

/**
 * returns a string of the log directory
 */
char* Battery::getLogDirectory()
{

    return logDir;
}

/**
 * Returns true if we are authorized to start charging.
 */
bool Battery::startCharging(void)
{
    return startCharge;
}

bool Battery::isCheckedIn(void)
{
    if(checkedIn == 1) {
        return true;
    } else {
        return false;
    }
}

void Battery::disableInt(void)
{
    // Detaches all interrupts:
    txWatch->detach();
    delay->detach();
    nrf->setPwrDown();

    return;
}