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-10-03
Revision:
16:2482d226cf4d
Parent:
15:c944ee3c8a5b

File content as of revision 16:2482d226cf4d:

#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);
Timer cycleTimer;
CycleWatch cycleWatch;

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


/******************************************************************************/
/***                      <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,0);
    lcd.printf("                    "); // Clear the Line using Spaces (Emptyness) - Note one line is 20 Characters
    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]; // assume no more than 15 conditions will be needed
    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;
                
                //if numParam = 0 at this point, it means there's only one condition to check... need to add a member to the vector since none have been added yet
                if (numParam == 0)
                    paramCondition.push_back(ConditionOp());
                    
                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(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;
            
        //Only one condition to check
        if (numParam == 1){
            tempCombinedCondition.value = paramCondition[k].value;
            tempCombinedCondition.op = NONE; 
            combinedCondition.push_back(tempCombinedCondition);        
        }
        
        else{
            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
    if (numParam > 1){
        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(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: cycleCommand>                            *****************************/
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/

int cycleCommand(LineData &lineData, int cycleState){

    // if cycleState is 1, then initialize the cycle
    if (cycleState == 1){
                
        //Get the Condition value for number of times to loop
        string numCycles = lineData.word[1];
        int numValuesFound = sscanf(numCycles.c_str(), "%d", &cycleWatch.numCycles);
        if (numValuesFound < 1){
            ErrorOut("Parameter Unknown, loopCondition Value can't be converted", lineData.lineNumber);
            return -1;
        }
        
        
        //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
            
            
        //****************//
        //Get the person to dynamically select what number of cycles they'd like to do, starting with the read value as the base value
        //****************//
        lcd.cls(); //clear the display
        lcd.setAddress ( 0, 2 );
        lcd.printf( "<<ACTION REQUIRED>>" );
        lcd.setAddress ( 0, 3 );
        lcd.printf( "<Press Sel to start>" );
        while (!buttons.readSel()){
            if(buttons.readUp() && cycleWatch.numCycles < 999999 ){
                cycleWatch.numCycles++;
                wait(0.05); //so that the speed of changing the numbers is more controllable, should mean you can move 20 digits per second
            }
            else if (buttons.readDown() && cycleWatch.numCycles > 0 ){
                cycleWatch.numCycles--;
                wait(0.05); //so that the speed of changing the numbers is more controllable, should mean you can move 20 digits per second
            }
            lcd.setAddress ( 0, 0 );
            lcd.printf( "<Num Cycles: %d >" , cycleWatch.numCycles );
        }
        
        //Initialize the counter variable of the struct, and start the cycle timer
        cycleWatch.counter = 0;
        cycleTimer.start();
        
        //Update the LCD to display the desired data
        lcd.cls(); //clear the display            
        //Output the Avg Cycle Time
        cycleTimer.stop(); 
        cycleWatch.totalCycleTime += cycleTimer.read();
        lcd.setAddress(0,1);
        lcd.printf("Avg t(sec): 0.000");
        
        //Output Cycle Number
        cycleWatch.counter++;
        lcd.setAddress(0,0);
        lcd.printf("Cycle %d of %d", cycleWatch.counter, cycleWatch.numCycles);


        //get the next line in order to get the line address to return to 
        int returnValue = getNextLine(selectedFile, lineData);     
        //if getNextLine returned an error, then error out
        if (returnValue == -1)
            return -1;
        
        //save the staring location of this cycle loop 
        cycleWatch.startAddress = lineData.lineAddress;
        cycleWatch.startLineNumber = lineData.lineNumber; 
        
        //seek back a line, so once this function returns, the next line reteived is that of the next line that should be processed.... basically... so we don't skip a line
        lineData.lineNumber = cycleWatch.startLineNumber - 1;
        int seekFailure = fseek(selectedFile, cycleWatch.startAddress, SEEK_SET); //fseek returns 0 on success
        if (seekFailure){
            ErrorOut("Init Cycle    Failed to seek line", lineData.lineNumber); //Spaces make it look nice on the LCD
            return -1;
        }
        
        //Restart the timer for the cycle
        cycleTimer.reset();
        cycleTimer.start();
    }
    
    
    
    
    // if cycleState is 1, then check for ending conditions
    else if (cycleState == 0){
    
        //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(); 
        cycleWatch.totalCycleTime += cycleTimer.read();
        lcd.setAddress(0,1);
        lcd.printf("Avg t(sec): %1.3f", (cycleWatch.totalCycleTime / cycleWatch.counter));
        
        //Output Cycle Number
        cycleWatch.counter++;
        lcd.setAddress(0,0);
        lcd.printf("Cycle %d of %d", cycleWatch.counter, cycleWatch.numCycles);

        
        if (cycleWatch.counter <= cycleWatch.numCycles){
            
            //seek back to the start of the cycle loop
            lineData.lineNumber = cycleWatch.startLineNumber - 1;
            int seekFailure = fseek(selectedFile, cycleWatch.startAddress, SEEK_SET); //fseek returns 0 on success
            if (seekFailure){
                ErrorOut("In Cycle      Failed to seek line", lineData.lineNumber); //Spaces make it look nice on the LCD
                return -1;
            }
                
            //Restart the timer for the next cycle
            cycleTimer.reset();
            cycleTimer.start();
        }
    }
    
    return 0; //Return Success, no value is being sent so don't return 1
 }   
 
 
 
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/
/**************************                        <FUNCTION: loopCommand>                            *****************************/
/**********************************************************************************************************************************/
/**********************************************************************************************************************************/

int loopCommand(LineData &lineData){
    
    //Get the Condition value for number of times to loop
    string loopCondition = lineData.word[1];
    int loopConditionValue = 0;
    int loopConditionState = 0; //State 1 = device condtition, State 2 = numerical condition
    
    LineData conditionLine;
    //if the loop is supposed to happen under specific device conditions
    if (loopCondition.compare("condition") == 0){
                
        loopConditionState = 1;
        
        //extract the command condition to be checked each loop
        int i = 2, funcNumWords = 0;
        for(i = 2; i < lineData.numWords; i++){
            conditionLine.word[funcNumWords] = lineData.word[i];
            funcNumWords++;
        }

        conditionLine.numWords = funcNumWords;
        conditionLine.lineAddress = lineData.lineAddress;
        conditionLine.lineNumber = lineData.lineNumber;  
    }
    
    //if the second word isn't condition, it means it's a number
    else{
        loopConditionState = 2;
        
        int numValuesFound = sscanf(loopCondition.c_str(), "%d", &loopConditionValue);
        if (numValuesFound < 1){
            ErrorOut("Parameter Unknown, loopCondition Value can't be converted", lineData.lineNumber);
            return -1;
        }
        
        //loop condition must be greater than 0
        if (loopConditionValue <= 0){
            ErrorOut("Loop Condition must be greater than 0", lineData.lineNumber);
            return -1;
        }
    }
    
    
    int loopStartAddress = 0, loopLineNumber = 0, firstLineOfLoop = 1;
    int counter = 1, checkEnd = 0, returnValue, conditionMet = 0;
    

    //Before starting the loop, get the state of the device conditions
    if (loopConditionState == 1){
        conditionMet = interpretCommand(conditionLine);
        if (conditionMet == -1)
            return -1; //if the interpretCommand returned an error, then error out
            
        //condition met, so skip to end of loop
        if (conditionMet == 1){
            int checkEnd = 0, returnValue = 0;
            while (checkEnd != 3){
            
            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(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;
            }
        }
    }

    
    while (!conditionMet){
        
        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(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
                
            //Check whether the we should stop looping based on the state that the loop is based on (device conditional / numerical conditional)
            if (loopConditionState == 1){
                conditionMet = interpretCommand(conditionLine);
                if (conditionMet == -1)
                    return -1; //if the interpretCommand returned an error, then error out
            }
            else if (loopConditionState == 2){                
                if (counter >= loopConditionValue)
                     conditionMet = 1;
            }
            
            //if the condition has not been met, then seek back to the beginning of the loop
            if (!conditionMet){
                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;    
        }
        
        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 interprettingErrorFlag = 0;
int enableErrors = 0;

class ErrorCondition{
    
    public:
        LineData errorToWatch;
        LineData errorFix;    
        int hasFix;
};

//vector<ErrorCondition> errorMonitors; //Initialize vector of errors to monitor
ErrorCondition errorMonitors[5];
int numErrorsConditions = 0;


int interpretCommand(LineData &lineData){
        
    //Monitor the Kill Switch, pause the system as needed
    if(killSw == 1){
        //place all devices into the pause functionality
        int i = 0;
        for(i = 0; i < devices.size(); i++)
            devices[i]->pause();
        
        cycleTimer.stop(); //pause the cycle timer
        
        //Notify the User of the System Kill
        lcd.setAddress(0,3);
        lcd.printf("      Killed!       ");
    
        int flag = 0;
        while (flag == 0){
            while(killSw == 1);
            wait(0.04);
            if (killSw == 0)
                flag = 1;
        }
         
        //place all devices into the resume functionality
        for(i = 0; i < devices.size(); i++)
            devices[i]->resume();
        
        lcd.setAddress(0,3);
        lcd.printf("                    "); // Clear the Line using Spaces (Emptyness) - Note one line is 20 Characters
        cycleTimer.start(); //start the cycle timer
    }
    
    
    //Monitors the conditions to watch for erroring, and pauses system if any of the conditions turn out to be true
    if (!interprettingErrorFlag && enableErrors){
        int j = 0, error = -1, numError = 0;
        for(j = 0; j < numErrorsConditions; j++){
            int checkCondition = 0;
            
            interprettingErrorFlag = 1;   
            checkCondition = interpretCommand(errorMonitors[j].errorToWatch);
            interprettingErrorFlag = 0;

            //if Condition is true, that means the error occurred
            if (checkCondition == 1){
                numError++;

                //if the error has a Fix / Reset command, do it
                if (errorMonitors[j].hasFix){
                    interprettingErrorFlag = 1;
                    int returnValue = interpretCommand(errorMonitors[j].errorFix); //Fix / Reset the error based on how the user justified to do so
                    interprettingErrorFlag = 0;
                    if (returnValue == -1)
                        return -1;
                }
                    
                error = j; //Record index of error, will display only one error, but error will be last error that was true in the list
            }
            
            else if (checkCondition == -1)
                return -1;
        }
      
        if (numError){
            char errorMsg[100] = "errorWatch!  Item: ";
            strcat(errorMsg, errorMonitors[error].errorToWatch.word[0].c_str()); //Send the first word of the error condition to help find out what the error was
            ErrorOut(errorMsg, numError);

            //place all devices into the pause functionality
            int i = 0;
            for(i = 0; i < devices.size(); i++)
                devices[i]->pause();
            
            cycleTimer.stop(); //pause the cycle timer
            
            //LCD has already been adjusted with the ErrorMonitor function
            //Simply wait for the user to press select in order to acknowledge the issue and try to fix it
            while(!buttons.readSel());
                        
            //place all devices into the resume functionality
            for(i = 0; i < devices.size(); i++)
                devices[i]->resume();
            
            cycleTimer.start(); //start the cycle timer
            
            lcd.cls(); //clear the display  
        }
    }

        
    
    /******************************************************************************/
    /***                        <Functionality: device>                         ***/
    /******************************************************************************/
    
    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;   
            }         
        }
    }
    
    
    
    /******************************************************************************/
    /***                     <Functionality: errorWatch>                        ***/
    /******************************************************************************/

    else if (lineData.word[0].compare("errorWatch") == 0){
        //line looks like: error, _Condition_, FIX(?), _Command_
        //create a temp LineData variable to store the correct information of the error Condition (discluding the first "error" keyword  
        ErrorCondition tempError = ErrorCondition();
        LineData tempLine;
 
        int i = 1, funcNumWords = 0;
        for (i = 1; i < lineData.numWords; i++){    
            
            //if the keyword FIX is not this word, than add it to the LineData
            if (lineData.word[i].compare("FIX") != 0){
                tempLine.word[funcNumWords] = lineData.word[i];  
                funcNumWords++;   
            }     
            
            //if the keyword FIX was found, this means there's a FIX / Reset command if the specified error being monitored occurs
            else if (lineData.word[i].compare("FIX") == 0){
                tempError.hasFix = 1;
                
                //Save the line as the "Error to Watch" value
                tempLine.numWords = funcNumWords;
                tempLine.lineAddress = lineData.lineAddress;
                tempLine.lineNumber = lineData.lineNumber;
                tempError.errorToWatch = tempLine;
                
                //reset lineData and num words for the command / function in order to process the FIX command into the error condition
                funcNumWords = 0;
                tempLine = LineData(); //reset tempLine
            }                
        }
        
        //if the error has a fix, it means that it already process the "Error to Watch" but needs to process the "Error Fix" into the error condition
        if (tempError.hasFix == 1){
            //Save the line as the "Error Fix" value
            tempLine.numWords = funcNumWords;
            tempLine.lineAddress = lineData.lineAddress;
            tempLine.lineNumber = lineData.lineNumber;
            tempError.errorFix = tempLine;
        }
        
        //if the error does not have a fix, that means it still has to process the "Error to Watch" into the error condition
        else{
            //Save the line as the "Error to Watch" value
            tempLine.numWords = funcNumWords;
            tempLine.lineAddress = lineData.lineAddress;
            tempLine.lineNumber = lineData.lineNumber;
            tempError.errorToWatch = tempLine;
        }

        //if DummyMode, send error Condition to the interpretCommand so that we can check the syntax
        if (DummyMode){
            int returnValue1 = interpretCommand(tempError.errorToWatch);

            //if this error has a fix then check the syntax
            int returnValue2 = 0;
            if (tempError.hasFix)
                returnValue2 = interpretCommand(tempError.errorFix);

            if (returnValue1 == -1 || returnValue2 == -1)
                return -1;
        }
  
        errorMonitors[numErrorsConditions] = tempError;
        numErrorsConditions++;
    }
    
    
    /******************************************************************************/
    /***                        <Functionality: delay>                          ***/
    /******************************************************************************/
    
    else if (lineData.word[0].compare("delay") == 0){
        
        if (lineData.numWords != 2){
            ErrorOut("Incorrect number of parameters", lineData.lineNumber);
            return -1;
        }
        
        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){
            
            //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
            
            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 or equal to 0", lineData.lineNumber);
            return -1;
        }
    }
    
    /******************************************************************************/
    /***                        <Functionality: pause>                          ***/
    /******************************************************************************/
    
    else if (lineData.word[0].compare("pause") == 0){
        
        if (lineData.numWords != 1){
            ErrorOut("Incorrect number of parameters", lineData.lineNumber);
            return -1;
        }
        
        lcd.cls(); //clear the display
        lcd.setAddress(0,0);
        lcd.printf("System Pause");
        lcd.setAddress(0,2);
        lcd.printf("Line Number: %d", lineData.lineNumber);
        lcd.setAddress(0,3);
        lcd.printf("Press Sel to Resume");
        while(!buttons.readSel());
    }
    
    /******************************************************************************/
    /***                         <Functionality: GoTo>                          ***/
    /******************************************************************************/
    
    else if (lineData.word[0].compare("GoTo") == 0){
        
        if (lineData.word[1].compare("label") == 0){
            
            if (lineData.numWords != 3){
                ErrorOut("Incorrect number of parameters", lineData.lineNumber);
                return -1;
            }
        
            int labelExists = 0;
            for(vector<GoToLabel>::iterator it=GoToLabels.begin(); it < GoToLabels.end(); it++){
                if (lineData.word[1].compare((*it).name) == 0)
                    labelExists = 1;
            }

            //if the label does not exist then add it to the vector
            //if it turned out to exist then do nothing
            if (!labelExists){
                GoToLabel tempLabel;
                tempLabel.name = lineData.word[2];              //Save the Label Name
                tempLabel.lineAddress = lineData.lineAddress;   //Save the Line Address
                tempLabel.lineNumber = lineData.lineNumber;     //Save the Line Number
                GoToLabels.push_back(tempLabel);
            }
        }
        
        //if the second word was not "label," then it means the word should correspond to an already created GoTo Label
        else{
            
            if (lineData.numWords != 2){
                ErrorOut("Incorrect number of parameters", lineData.lineNumber);
                return -1;
            }  
            
            //All syntax checking done by this point, if Dummy then return success in order to check the code
            //Unable to continue further checking since an aspect of DummyMode run through is to collect the GoTo Label Locations
            //so that we have them in address
            if (DummyMode)
                return 0; //Function operated successfully but doesn't return a value
                    
            int i = 0, labelFound = -1;
            for(i = 0; i < GoToLabels.size(); i++){
                if (lineData.word[1].compare(GoToLabels[i].name) == 0){
                    labelFound = i;
                }
            }
            
            //if the label was not found then error out
            if (labelFound == -1){
                ErrorOut("No label     match found in the  system", lineData.lineNumber); //Spaces make it look nice on LCD
                return -1;   
            }
            
            //if the label was found, then seek to that GoTo position
            else{
                                    
                int seekFailure = fseek(selectedFile, GoToLabels[labelFound].lineAddress, 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 = GoToLabels[labelFound].lineNumber - 1; //set the lineNumber value                
            }
        }
    }
    
    /******************************************************************************/
    /***                         <Functionality: loop>                          ***/
    /******************************************************************************/
    
    else if (lineData.word[0].compare("loop") == 0){
        enableErrors = 1;
        return loopCommand(lineData); //Process the loop command and return the value that it returns
   }
       
    /******************************************************************************/
    /***                     <Functionality: condition>                         ***/
    /******************************************************************************/
    else if (lineData.word[0].compare("condition") == 0){
        enableErrors = 1;
       return conditionCommand(selectedFile, lineData); //Process the condition command and return the value that it returns
    }

    /******************************************************************************/
    /***                       <Functionality: cycle>                           ***/
    /******************************************************************************/
    else if (lineData.word[0].compare("cycle") == 0){
        enableErrors = 1;
       return cycleCommand(lineData, 1); //Sending 1 means it's initializing
   }
       
    /******************************************************************************/
    /***                         <Functionality: end>                           ***/
    /******************************************************************************/
    //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 if (lineData.word[1].compare("cycle") == 0){
            cycleCommand(lineData, 0); //Sending 0 means it's checking for the ending
            return 5;
        } 
        
        else{
            ErrorOut("Unknown function ending", lineData.lineNumber);
            return -1;
        }
    }
    
    /******************************************************************************/
    /***                 <Functionality: local_name Checking>                   ***/
    /******************************************************************************/
    
    //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;
    mkdir("/sd", 0777);
    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");
    
    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>            ***/
    /******************************************************************************/
while(1){
    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(lineData); //interpret the line data
                
        if (checkEnd == 2){            
            //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
                devices.erase(devices.begin(), devices.end()); //remove the devices vector from memory so that we don't duplicate items on the real run through
                int k = 0;
                for (k = 0; k < numErrorsConditions; k++)
                    errorMonitors[k] = ErrorCondition();
                numErrorsConditions = 0;
            }
            else
                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;
        }        
    }
    
    //Exit the program and remove needed items from memory
    GoToLabels.erase(GoToLabels.begin(), GoToLabels.end()); //remove the GoToLabels vector from memory
    devices.erase(devices.begin(), devices.end()); //remove the devices vector from memory
    
    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("Cycle Power");
    
    fclose(selectedFile);
    return 1;
    
    /*while(!buttons.readBack());
    rewind(selectedFile);*/
}
 }