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);*/ } }