Component Test's Software to work with "Universal Controller Box" - Software is an interpreter or "compiler" for programs to be done with a .txt file and read off of the SD Card

Dependencies:   BridgeDriver FrontPanelButtons MCP23017 SDFileSystem TextLCD mbed

main.cpp

Committer:
mehatfie
Date:
2014-09-24
Revision:
10:e8db892fbc52
Parent:
9:5a0c4c6e39c7
Child:
11:bc9cd2869f95

File content as of revision 10:e8db892fbc52:

#include "mbed.h"
#include "LocalPinNames.h"
#include "BridgeDriver.h"
#include "FrontPanelButtons.h"
#include "TextLCD.h"
#include "SDFileSystem.h"
#include "Initialization.hpp"
//#include "mainFunctions.hpp"
#include "TextFile.h"
#include <stdio.h>
#include <string>
#include <stdlib.h>
#include <fstream>
#include <vector>
using std::string;

FrontPanelButtons buttons(&i2c);

//extern "C" void mbed_reset(); //enable software reset of code

    
int cyclePrograms(vector<string>, int, int, int);

void resetLineData(LineData &); //reset and all variables of the Line Data Struct

int interpretCommand(FILE *, LineData &);

int loopCommand(FILE *, LineData &);

/******************************************************************************/
/***                      <Function: resetLineData>                         ***/
/******************************************************************************/

void resetLineData(LineData &lineData){
    
    lineData.lineNumber = 0;
    lineData.numWords = 0;
    lineData.lineAddress = 0;
}

/**********************************************************************************************************************************/
/**********************************************************************************************************************************/
/************************                        <FUNCTION: cyclePrograms>                            *****************************/
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/
   
int cyclePrograms(vector<string> files, int SIZE, int currIndex, int direction){
    
    int nextIndex = 0;
    switch(direction){
        case 0: //Cycle Back one File
            if ((currIndex - 1) < 0)
                nextIndex = SIZE - 1;
            else
                nextIndex = currIndex - 1;
            break;
        case 1: //Cycle Forward one File
            if ((currIndex + 1) >= SIZE)
                nextIndex = 0; 
            else
                nextIndex = currIndex + 1;
            break;
        case -1: //set the selectedFile to the currIndex (used for initialization)
            nextIndex = currIndex;
            break;
    }

    //Output file on Display 
    lcd.setAddress(0,3);
    lcd.printf("                   "); // Clear the Line using Spaces (Emptyness) - Note one line is 20 Characters
    wait(.2);
    lcd.setAddress(0,3);
    lcd.printf("%s", files[nextIndex]);
    
    return nextIndex; // Return the file index in the Array         
}
            
    
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/
/**************************                        <FUNCTION: conditionCommand>                            *****************************/
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/

//Create an enum map of the positble conditions
enum  ConditionType{xAND, AND, xOR, OR, NONE};                                  

struct ConditionOp{
    
    int value;          //returned value of the interpret function: 1 = meets criteria, 0 = criteria not met, -1 = failed to interpret
    ConditionType op;   //operator that follows the given parameter: x val 2 AND y val 3, if this ConditionOp is for x, then the value will be AND
};

int conditionCommand(FILE *selectedFile, LineData &lineData){
                
    //Initialize variables    
    LineData param[15];
    vector<ConditionOp> paramCondition;
 
    
    //Fill the param Vector with Line structs of each individual device, this way we can send the Line struct to the appropriate interpret function without modification within the function itself
    //this line reads: Condition, data_for_param1, CONDTITION_OP1, data_for_param2, CONDTITION_OP2, data_for_param3......
    //Staring index of first data parameter is the 2nd word, therefore 1
    int i = 1, numParam = 0, paramNumWords = 0;
    for (i = 1; i < lineData.numWords; i++){
                       
        // if the word is not an AND or an OR, it must mean it's for the specific function
        // set the current parameter's next word to be equal to the current word we're checking
        // increase number of words that the parameter has
        if (lineData.word[i] != "AND" && lineData.word[i] != "xAND" && lineData.word[i] != "OR" && lineData.word[i] != "xOR"){

            param[numParam].word[paramNumWords] = lineData.word[i];
            paramNumWords++;
            
            //if this is the last word in the line....
            if(i == (lineData.numWords - 1)){
                param[numParam].numWords = paramNumWords;
                paramCondition[numParam].op = NONE;
                numParam++;
            }
            
        }
            
        // if the word is an AND or an OR, it must mean the last function has been completely identified
        // set the parameters number of Words value to the calculated value
        // increase the number of Parameters (the current parameter function we're filling)
        else if (lineData.word[i].compare("AND") == 0 || lineData.word[i].compare("xAND") == 0 || lineData.word[i].compare("OR") == 0 || lineData.word[i].compare("xOR") == 0){
            
            param[numParam].numWords = paramNumWords;
            
            paramCondition.push_back(ConditionOp());
            if (lineData.word[i].compare("AND") == 0)
                paramCondition[numParam].op = AND;
            else if (lineData.word[i].compare("xAND") == 0)
                paramCondition[numParam].op = xAND;
            else if (lineData.word[i].compare("OR") == 0)
                paramCondition[numParam].op = OR;
            else if (lineData.word[i].compare("xOR") == 0)
                paramCondition[numParam].op = xOR;

            numParam++; // increase the index of param
            paramNumWords = 0; // reset the number of words
        }    
    }
    
    
    //send the data parameters in order to get them interpreted by the appropriate device
    //if the value it's checking for meets the criteria you want, the device should return 1, if it doesn't meet the criteria the device should return 0
    int j = 0, k = 0;
    for (j = 0; j < numParam; j++){
        paramCondition[j].value = interpretCommand(selectedFile, param[j]);
        
        //error out if the interpretted command returned an error
        if (paramCondition[j].value == -1)
            return -1;
    }


    //create the combined Condition vector (take care of this xAND and xOR statements and combine them into one so that the whole vector is just AND's and OR's)
    //this should make the xAND's / xOR's into a single member of the combinedCondition vector
    enum ConditionType prevCondition = NONE; 
    vector<ConditionOp> combinedCondition;
    ConditionOp tempCombinedCondition;
    int first = 1, last = 0;
    for (k = 0; k < numParam; k++){

        if (k == numParam - 1)
            last = 1;
            
       if (!last){
            if (paramCondition[k].op != xAND && paramCondition[k].op != xOR && paramCondition[k + 1].op != xAND && paramCondition[k + 1].op != xOR){
                //AND
                if (paramCondition[k].op == AND){
                    if (!first && prevCondition != xAND && prevCondition != xOR)
                        combinedCondition.back().value = combinedCondition.back().value && paramCondition[k + 1].value;
                    else if (first || prevCondition == xAND || prevCondition == xOR){
                        tempCombinedCondition.value = paramCondition[k].value && paramCondition[k + 1].value; 
                        combinedCondition.push_back(tempCombinedCondition);
                        first = 0;
                    }    
                    prevCondition = AND;
                }
                
                //OR
                else if (paramCondition[k].op == OR){
                    if (!first && prevCondition != xAND && prevCondition != xOR)
                        combinedCondition.back().value = combinedCondition.back().value || paramCondition[k + 1].value;
                    else if (first || prevCondition == xAND || prevCondition == xOR){
                        tempCombinedCondition.value = paramCondition[k].value || paramCondition[k + 1].value; 
                        combinedCondition.push_back(tempCombinedCondition);
                        first = 0;
                    }    
                    prevCondition = OR;
                }
            }
            
            // first value is something, not exclusive, but next values are exclusive
            else if (first && (paramCondition[k].op == AND || paramCondition[k].op == OR) && (paramCondition[k + 1].op == xAND || paramCondition[k + 1].op == xOR)){
                tempCombinedCondition.value = paramCondition[k].value;
                tempCombinedCondition.op = paramCondition[k].op;
                combinedCondition.push_back(tempCombinedCondition);
                prevCondition = paramCondition[k].op;
                first = 0;
            }
            
            else{   
                //xAND
                if (paramCondition[k].op == xAND){
                    if (combinedCondition.size() == 0){ // No values so start a new combinedCondition
                        tempCombinedCondition.value = paramCondition[k].value && paramCondition[k + 1].value;
                        tempCombinedCondition.op = xAND;
                        combinedCondition.push_back(tempCombinedCondition);
                        prevCondition = xAND;
                    }
                    else{
                         if (combinedCondition.back().op == xAND){ // AND the value to the back most combinedCondition
                            combinedCondition.back().value = combinedCondition.back().value && paramCondition[k + 1].value;
                            prevCondition = xAND;
                        }
                        else if (combinedCondition.back().op != xAND){ // Start a new combinedCondition
                            tempCombinedCondition.value = paramCondition[k].value && paramCondition[k + 1].value; 
                            tempCombinedCondition.op = xAND;
                            combinedCondition.push_back(tempCombinedCondition);
                            prevCondition = xAND;
                        }
                    }
                        
                }
                
                //xOR
                else if (paramCondition[k].op == xOR){
                    if (combinedCondition.size() == 0){ // No values so start a new combinedCondition
                        tempCombinedCondition.value = paramCondition[k].value || paramCondition[k + 1].value; 
                        tempCombinedCondition.op = xOR;
                        combinedCondition.push_back(tempCombinedCondition);
                        prevCondition = xOR;
                    }
                    else{
                         if (combinedCondition.back().op == xOR){ // OR the value to the back most combinedCondition
                            combinedCondition.back().value = combinedCondition.back().value || paramCondition[k + 1].value;
                            prevCondition = xOR;
                        }
                        else if (combinedCondition.back().op != xOR){ // Start a new combinedCondition
                            tempCombinedCondition.value = paramCondition[k].value || paramCondition[k + 1].value; 
                            tempCombinedCondition.op = xOR;
                            combinedCondition.push_back(tempCombinedCondition);
                            prevCondition = xOR;
                        }
                    }
                        
                }
                
                // Since the k + 1 value is included in the xAND or xOR exclusively, skip checking that value, and add the appropriate AND / OR as the
                // operator of this exclusive xAND / xOR set
                if ((paramCondition[k + 1].op == AND || paramCondition[k + 1].op == OR) && (prevCondition == xAND || prevCondition == xOR)){
                    combinedCondition.back().op = paramCondition[k + 1].op;
                    k++;
                }
                
            }
       }
            
        
        // the last value was not included in any combination, since directly before the last value was an xAND / xOR set that
        // included the very last AND / OR as the set's operator, yet there is still another value that has not been combined, as it is supposed
        // to be AND /OR to the exclusive xAND / xOR set
        else if (last && (prevCondition == xAND || prevCondition == xOR) && (combinedCondition.back().op == AND || combinedCondition.back().op == OR)){
            tempCombinedCondition.value = paramCondition[k].value;
            tempCombinedCondition.op = NONE; 
            combinedCondition.push_back(tempCombinedCondition);
        }
        
        //reset the tempCombinedCondition variable
        tempCombinedCondition = ConditionOp();
    }
        
                
    // run through all values in the combined Condition vector, AND'ing / OR'ing as appropriate
    // in the end, the last value in the array should be the final condition of the Condition statement... whether it was successful or failed
    for (i = 0; i < (combinedCondition.size() - 1); i++){
        if (combinedCondition[i].op == AND)
            combinedCondition[i + 1].value = combinedCondition[i].value && combinedCondition[i + 1].value;
        else if (combinedCondition[i].op == OR)
            combinedCondition[i + 1].value = combinedCondition[i].value || combinedCondition[i + 1].value;
    }
    
    
    //All syntax checking done by this point, if Dummy then return success in order to check the code within the Condition
    if (DummyMode)
        return 0; //Function operated successfully but doesn't return a value
        
        
    int conditionSuccess = combinedCondition.back().value; //value is the success(1) or failure(0) of the condition statement

    int checkEnd = 0, returnValue;
    if (!conditionSuccess){

        while (checkEnd != 4){
            
            returnValue = getNextLine(selectedFile, lineData);
            
            //if getNextLine returned an error, then error out
            if (returnValue == -1)
                return -1;
            
            // check if the first word is an end command (avoids interpreting functions that perform actions)
            if (lineData.word[0].compare("end") == 0)   
                checkEnd = interpretCommand(selectedFile, lineData);
        
            if (checkEnd == 4) // custom return value for this function
                return 0; //Function operated successfully but doesn't return a value
            else if (checkEnd == -1) //if interpretCommand returned an error, then error out
                return -1;
        }
    }
    
    // Return success as the function either met the condition and will continue from the next line, or
    // failed to meet the condition and ran through the lines inside the condition until "end condition" was found, therefore
    // the program will proceed from the line after the "end condition" line
    return 0; //Function operated successfully but doesn't return a value
}      
   

/**********************************************************************************************************************************/
/**********************************************************************************************************************************/
/**************************                        <FUNCTION: loopCommand>                            *****************************/
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/

int loopCommand(FILE *selectedFile, LineData &lineData){
        
    //Get the Condition value for number of times to loop
    string loopCondition = lineData.word[1];
    int loopConditionValue = 0;
    
    int numValuesFound = sscanf(loopCondition.c_str(), "%d", &loopConditionValue);
    if (numValuesFound < 1){
        ErrorOut("Parameter Unknown, loopCondition Value can't be converted", lineData.lineNumber);
        return -1;
    }
    
    int loopStartAddress = 0, loopLineNumber = 0, firstLineOfLoop = 1;
        
    lcd.setAddress(0,0);
    lcd.printf("Cycle 1 of %d", loopConditionValue);
        
    float totalLoopTime = 0;
    Timer cycleTimer;
    cycleTimer.reset();
    cycleTimer.start();
       
    int counter = 1, checkEnd = 0, returnValue;
    while (counter <= loopConditionValue){
        
        returnValue = getNextLine(selectedFile, lineData); 
        
        //if getNextLine returned an error, then return error out
        if (returnValue == -1)
            return -1;
                
        //Must get the address before entering the interpret command
        // if a Condition command is immediately after, and the condition fails, then
        // the interpret command will return the line at the "end condition" line, and therefore
        // set the loop's first line to be the "end condition" line, if interpretCommand is called BEFORE setting the first loop line address
        if (firstLineOfLoop){
            loopStartAddress = lineData.lineAddress; //Save the Line Address
            loopLineNumber = lineData.lineNumber;    //Save the Line Number
            firstLineOfLoop = 0;
        }
           
        checkEnd = interpretCommand(selectedFile, lineData);

        //Increase the loop counter and go back to the beginning of the loop
        if (checkEnd == 3){
            
            //All syntax checking done by this point, if Dummy then return success in order to check the code, no need to loop again
            if (DummyMode)
                return 0; //Function operated successfully but doesn't return a value
                
            //Output the Avg Cycle Time
            cycleTimer.stop(); 
            totalLoopTime += cycleTimer.read();
            
            lcd.setAddress(0,1);
            lcd.printf("Avg t(sec): %1.3f", (totalLoopTime / counter));
            
            //Output Cycle Number
            counter++;
            lcd.setAddress(0,0);
            lcd.printf("Cycle %d of %d", counter, loopConditionValue);
            
            int seekFailure = fseek(selectedFile, loopStartAddress, SEEK_SET); //fseek returns 0 on success
            if (seekFailure){
                ErrorOut("In Loop      Failed to seek line", lineData.lineNumber); //Spaces make it look nice on the LCD
                return -1;
            }
            
            lineData.lineNumber = loopLineNumber - 1;
            checkEnd = 0;
            
            //Restart the timer for the next loop
            cycleTimer.reset();
            cycleTimer.start();
        }
        else if (checkEnd == -1) //if interpretCommand returned an error, then return error out
                return -1;
    }
                
    return 0; //Return Success, no value is being sent so don't return 1
 }   

/**********************************************************************************************************************************/
/**********************************************************************************************************************************/
/*************************                        <FUNCTION: interpretCommand>                            *************************/
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/

int interpretCommand(FILE *selectedFile, LineData &lineData){
        
    if (lineData.word[0].compare("device") == 0){

        int i = 0, deviceFound = -1;
        for (i = 0; i < numDevices; i++){
            if (lineData.word[2].compare(DeviceNames[i]) == 0){
                deviceFound = i;
            }
        }

        //if the device type does not match any known type, error out
        if (deviceFound == -1){
            ErrorOut("No device    match found in the  system", lineData.lineNumber); //Spaces make it look nice on LCD
            return -1;   
        }
        
        //Add device to the array of devices and initialize it
        else{
            devices.push_back(Device::newDevice(deviceFound, lineData.word[1], lineData));
            devices.back()->name = lineData.word[1];
            
            //since the constructor cannot return a value, it will trip the error Flag if something is wrong, check that flag, and return error if it has been tripped
            if (devices.back()->errorFlag == 1){
                ErrorOut("Error initializing device", lineData.lineNumber);
                return -1;   
            }         
        }
    }
    
    else if (lineData.word[0].compare("delay") == 0){
        
        //All syntax checking done by this point, if Dummy then return success in order to check the code, no need wait for a delay
        if (DummyMode)
            return 0; //Function operated successfully but doesn't return a value
            
        string duration = lineData.word[1];
        int durationValue = 0;
        int numValuesFound = sscanf(duration.c_str(), "%d", &durationValue);
        
        if (numValuesFound < 1){
            ErrorOut("Parameter Unknown, Duration Value can't be converted", lineData.lineNumber);
            return -1;
        }
        
        if (durationValue > 0){
            timer.reset();
            timer.start();
            while (timer.read_ms() < durationValue); //Do Nothing while the timer has not reached the duration
            timer.stop(); //Stop the Timer
        }
        else{
            ErrorOut("Duration value is less than 0", lineData.lineNumber);
            return -1;
        }
    }
    
    else if (lineData.word[0].compare("loop") == 0)
        return loopCommand(selectedFile, lineData); //Process the loop command and return the value that it returns
       
    else if (lineData.word[0].compare("condition") == 0)
       return conditionCommand(selectedFile, lineData); //Process the condition command and return the value that it returns

    //end has custom return value for specific functions, since "end" is a common keyword amongst functions
    else if (lineData.word[0].compare("end") == 0){
        if (lineData.word[1].compare("program") == 0)
            return 2;
        else if (lineData.word[1].compare("loop") == 0)
            return 3;
        else if (lineData.word[1].compare("condition") == 0)
            return 4;
        
        else{
            ErrorOut("Unknown function ending", lineData.lineNumber);
            return -1;
        }
    }
    
    //not a keyword so check if it's a localName for a device
    else{
    
        int i = 0, deviceFound = -1;
        for (i = 0; i < devices.size(); i++){
            if (lineData.word[0].compare(devices[i]->name) == 0)
                deviceFound = i;
        }
                
        //no device was found that matched the local name, and this is also the last error check, meaning it can match no other potential keywords
        if (deviceFound == -1){
            ErrorOut("No device    match found in the  system", lineData.lineNumber); //Spaces make it look nice on LCD
            return -1;   
        }
        
        //Local Name matches a device, send line to that device in order to process the functionality
        else
            return devices[deviceFound]->interpret(lineData); //call the device specific interpret command, and return the value it returns
    }  
    
    return 0; //Return Success, no value is being sent so don't return 1
}


/**********************************************************************************************************************************/
/**********************************************************************************************************************************/
/******************************                        <FUNCTION: main>                            ********************************/
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/

int main() {
    
    fullInit(); //Initialize anything that's required to run the code (LCD)
    
    LineData lineData;
    resetLineData(lineData);

    /******************************************************************************/
    /***                 <Get all the Potential Programs>                       ***/
    /******************************************************************************/
    int numTextFiles = 0;
    vector<string> textFiles; //Assuming Maximum of 25 txt files will be on the SD Card        
    vector<string> filenames = readFileNames("/sd");  
    
    //Error check whether the SD Card exists and was able to be accessed.... or if there's no files on the SD Card
    if (filenames.size() == 0){
        ErrorOut("No Files     Found, or Directory can't be accessed", 0); //Spaces make it look nice on LCD
        return -1; //End program by returning in the main()
    }
    
    numTextFiles = getFileNamesWithoutExt(textFiles, filenames);
    
    //Error check whether the SD Card has any txt files in it's first directory
    if (numTextFiles == 0){
        ErrorOut("No Program   (.txt) Files Found  in first Directory", 0); //Spaces make it look nice on LCD
        return -1; //End program by returning in the main()
    }
    
    /******************************************************************************/
    /***                   <Select the Program txt File>                        ***/
    /******************************************************************************/
    int fileSelected = 0, selectedFileIndex = 0;

    lcd.cls(); //clear the display
    lcd.setAddress(0,1);
    lcd.printf("Select Your Program");
    lcd.setAddress(0,2);
    lcd.printf("Num Programs = %d", numTextFiles);
    
    uint8_t lastButState = 0;
    lcd.setCursor(TextLCD::CurOn_BlkOn); //turn blinking cursor on

    selectedFileIndex = cyclePrograms(textFiles, numTextFiles, selectedFileIndex, -1); //Initialize the first file to the screen
    while(!fileSelected) {
        
        uint8_t curButState = buttons.readBus();
        if(curButState != lastButState){
            lastButState = curButState;
            if(buttons.readRight())
                selectedFileIndex = cyclePrograms(textFiles, numTextFiles, selectedFileIndex, 1);
            else if(buttons.readLeft())
                selectedFileIndex = cyclePrograms(textFiles, numTextFiles, selectedFileIndex, 0);
            else if(buttons.readSel())
                fileSelected = 1;
        }
    }
    
    lcd.setCursor(TextLCD::CurOn_BlkOff); //turn blinking cursor off
    
    char selectedFileName[50];
    strcpy(selectedFileName, textFiles[selectedFileIndex].c_str());
    
    /******************************************************************************/
    /***                    <Open the Program txt File>                         ***/
    /******************************************************************************/
    
    //Create the string of the full directory and path to the program txt file
    char selectedFileFullName[100] = "/sd/";    //Assuming that no directory and file name will be longer than 100 characters
    strcat(selectedFileFullName, selectedFileName);
    strcat(selectedFileFullName, ".txt");
    
    FILE *selectedFile = fopen(selectedFileFullName, "r");
    
    //Error out of attempt to open the selected file was unsuccessful
    if(selectedFile == NULL) {
        ErrorOut("Unable to Open Selected File", 0); //Spaces make it look nice on LCD
        return -1; //End program by returning in the main()
    }
    
        
    /******************************************************************************/
    /***          <Start Running through the Program txt File Lines>            ***/
    /******************************************************************************/
      
    resetLineData(lineData); //Reset the values in the struct that holds the File / Line Data
    lcd.cls(); //clear the display   

    int endOfFile = 0, error = 0, returnValue, checkEnd;
    DummyMode = 1; //set Dummy Mode equal to 1 to simulate the first run through of the code 
    while (!endOfFile){
        
        //Re-initialize variables
        returnValue = 0;
        checkEnd = 0;
        
        returnValue = getNextLine(selectedFile, lineData); //get the next line of data
        
        //if getNextLine returned an error, then return error in main
        if (returnValue == -1)
            error = 1;
            
        checkEnd = interpretCommand(selectedFile, lineData); //interpret the line data
                
        if (checkEnd == 2)
            endOfFile = 1;
        else if (checkEnd == -1) //if interpretCommand returned an error, then return error in main
            error = 1;
            
        //Before erroring out, turn all devices off so that they power down
        if (error){
            if (!DummyMode){ //if it is Dummy Mode, then no functionality has actually been turned on, so no need to shut anything off
                for(vector<Device*>::iterator it=devices.begin(); it < devices.end(); it++)  
                    (*it)->off();
            }
            
            return -1;
        }
    
        //Dummy Mode will be turned on for the first run through, set it to 0 after the first run through,
        //as the syntax will be checked without erroring, and therefore it is possible to try and run the .txt file
        //Seek back to beginning of file
        if (DummyMode){
            DummyMode = 0;
            rewind(selectedFile); //seek to the beginning of the file
            resetLineData(lineData); //Reset the values in the struct that holds the File / Line Data
        }
    }
                
    lcd.cls(); //clear the display   
    lcd.setAddress(0,0);
    lcd.printf("END OF PROGRAM");
    lcd.setAddress(0,2);
    lcd.printf("To Restart...");
    lcd.setAddress(0,3);
    lcd.printf("Press BACK");
   
    while(!buttons.readBack());
    
    lcd.setAddress(0,1);
    //rewind(selectedFile);

 }