Component Test's Software to work with "Universal Controller Box" - Software is an interpreter or "compiler" for programs to be done with a .txt file and read off of the SD Card
Dependencies: BridgeDriver FrontPanelButtons MCP23017 SDFileSystem TextLCD mbed
main.cpp
- Committer:
- mehatfie
- Date:
- 2014-09-24
- Revision:
- 9:5a0c4c6e39c7
- Parent:
- 8:e9f836163229
- Child:
- 10:e8db892fbc52
File content as of revision 9:5a0c4c6e39c7:
#include "mbed.h" #include "LocalPinNames.h" #include "BridgeDriver.h" #include "FrontPanelButtons.h" #include "TextLCD.h" #include "SDFileSystem.h" #include "Initialization.hpp" //#include "mainFunctions.hpp" #include "TextFile.h" #include <stdio.h> #include <string> #include <stdlib.h> #include <fstream> #include <vector> using std::string; FrontPanelButtons buttons(&i2c); //extern "C" void mbed_reset(); //enable software reset of code int cyclePrograms(vector<string>, int, int, int); void resetLineData(LineData &); //reset and all variables of the Line Data Struct int interpretCommand(FILE *, LineData &); int loopCommand(FILE *, LineData &); /******************************************************************************/ /*** <Function: resetLineData> ***/ /******************************************************************************/ void resetLineData(LineData &lineData){ lineData.lineNumber = 0; lineData.numWords = 0; lineData.lineAddress = 0; } /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ /************************ <FUNCTION: cyclePrograms> *****************************/ /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ int cyclePrograms(vector<string> files, int SIZE, int currIndex, int direction){ int nextIndex = 0; switch(direction){ case 0: //Cycle Back one File if ((currIndex - 1) < 0) nextIndex = SIZE - 1; else nextIndex = currIndex - 1; break; case 1: //Cycle Forward one File if ((currIndex + 1) >= SIZE) nextIndex = 0; else nextIndex = currIndex + 1; break; case -1: //set the selectedFile to the currIndex (used for initialization) nextIndex = currIndex; break; } //Output file on Display lcd.setAddress(0,3); lcd.printf(" "); // Clear the Line using Spaces (Emptyness) - Note one line is 20 Characters wait(.2); lcd.setAddress(0,3); lcd.printf("%s", files[nextIndex]); return nextIndex; // Return the file index in the Array } /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ /************************** <FUNCTION: conditionCommand> *****************************/ /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ //Create an enum map of the positble conditions enum ConditionType{xAND, AND, xOR, OR, NONE}; struct ConditionOp{ int value; //returned value of the interpret function: 1 = meets criteria, 0 = criteria not met, -1 = failed to interpret ConditionType op; //operator that follows the given parameter: x val 2 AND y val 3, if this ConditionOp is for x, then the value will be AND }; int conditionCommand(FILE *selectedFile, LineData &lineData){ //Initialize variables LineData param[15]; vector<ConditionOp> paramCondition; //Fill the param Vector with Line structs of each individual device, this way we can send the Line struct to the appropriate interpret function without modification within the function itself //this line reads: Condition, data_for_param1, CONDTITION_OP1, data_for_param2, CONDTITION_OP2, data_for_param3...... //Staring index of first data parameter is the 2nd word, therefore 1 int i = 1, numParam = 0, paramNumWords = 0; for (i = 1; i < lineData.numWords; i++){ // if the word is not an AND or an OR, it must mean it's for the specific function // set the current parameter's next word to be equal to the current word we're checking // increase number of words that the parameter has if (lineData.word[i] != "AND" && lineData.word[i] != "xAND" && lineData.word[i] != "OR" && lineData.word[i] != "xOR"){ param[numParam].word[paramNumWords] = lineData.word[i]; paramNumWords++; //if this is the last word in the line.... if(i == (lineData.numWords - 1)){ param[numParam].numWords = paramNumWords; paramCondition[numParam].op = NONE; numParam++; } } // if the word is an AND or an OR, it must mean the last function has been completely identified // set the parameters number of Words value to the calculated value // increase the number of Parameters (the current parameter function we're filling) else if (lineData.word[i].compare("AND") == 0 || lineData.word[i].compare("xAND") == 0 || lineData.word[i].compare("OR") == 0 || lineData.word[i].compare("xOR") == 0){ param[numParam].numWords = paramNumWords; paramCondition.push_back(ConditionOp()); if (lineData.word[i].compare("AND") == 0) paramCondition[numParam].op = AND; else if (lineData.word[i].compare("xAND") == 0) paramCondition[numParam].op = xAND; else if (lineData.word[i].compare("OR") == 0) paramCondition[numParam].op = OR; else if (lineData.word[i].compare("xOR") == 0) paramCondition[numParam].op = xOR; numParam++; // increase the index of param paramNumWords = 0; // reset the number of words } } //send the data parameters in order to get them interpreted by the appropriate device //if the value it's checking for meets the criteria you want, the device should return 1, if it doesn't meet the criteria the device should return 0 int j = 0, k = 0; for (j = 0; j < numParam; j++){ paramCondition[j].value = interpretCommand(selectedFile, param[j]); //error out if the interpretted command returned an error if (paramCondition[j].value == -1) return -1; } //create the combined Condition vector (take care of this xAND and xOR statements and combine them into one so that the whole vector is just AND's and OR's) //this should make the xAND's / xOR's into a single member of the combinedCondition vector enum ConditionType prevCondition = NONE; vector<ConditionOp> combinedCondition; ConditionOp tempCombinedCondition; int first = 1, last = 0; for (k = 0; k < numParam; k++){ if (k == numParam - 1) last = 1; if (!last){ if (paramCondition[k].op != xAND && paramCondition[k].op != xOR && paramCondition[k + 1].op != xAND && paramCondition[k + 1].op != xOR){ //AND if (paramCondition[k].op == AND){ if (!first && prevCondition != xAND && prevCondition != xOR) combinedCondition.back().value = combinedCondition.back().value && paramCondition[k + 1].value; else if (first || prevCondition == xAND || prevCondition == xOR){ tempCombinedCondition.value = paramCondition[k].value && paramCondition[k + 1].value; combinedCondition.push_back(tempCombinedCondition); first = 0; } prevCondition = AND; } //OR else if (paramCondition[k].op == OR){ if (!first && prevCondition != xAND && prevCondition != xOR) combinedCondition.back().value = combinedCondition.back().value || paramCondition[k + 1].value; else if (first || prevCondition == xAND || prevCondition == xOR){ tempCombinedCondition.value = paramCondition[k].value || paramCondition[k + 1].value; combinedCondition.push_back(tempCombinedCondition); first = 0; } prevCondition = OR; } } // first value is something, not exclusive, but next values are exclusive else if (first && (paramCondition[k].op == AND || paramCondition[k].op == OR) && (paramCondition[k + 1].op == xAND || paramCondition[k + 1].op == xOR)){ tempCombinedCondition.value = paramCondition[k].value; tempCombinedCondition.op = paramCondition[k].op; combinedCondition.push_back(tempCombinedCondition); prevCondition = paramCondition[k].op; first = 0; } else{ //xAND if (paramCondition[k].op == xAND){ if (combinedCondition.size() == 0){ // No values so start a new combinedCondition tempCombinedCondition.value = paramCondition[k].value && paramCondition[k + 1].value; tempCombinedCondition.op = xAND; combinedCondition.push_back(tempCombinedCondition); prevCondition = xAND; } else{ if (combinedCondition.back().op == xAND){ // AND the value to the back most combinedCondition combinedCondition.back().value = combinedCondition.back().value && paramCondition[k + 1].value; prevCondition = xAND; } else if (combinedCondition.back().op != xAND){ // Start a new combinedCondition tempCombinedCondition.value = paramCondition[k].value && paramCondition[k + 1].value; tempCombinedCondition.op = xAND; combinedCondition.push_back(tempCombinedCondition); prevCondition = xAND; } } } //xOR else if (paramCondition[k].op == xOR){ if (combinedCondition.size() == 0){ // No values so start a new combinedCondition tempCombinedCondition.value = paramCondition[k].value || paramCondition[k + 1].value; tempCombinedCondition.op = xOR; combinedCondition.push_back(tempCombinedCondition); prevCondition = xOR; } else{ if (combinedCondition.back().op == xOR){ // OR the value to the back most combinedCondition combinedCondition.back().value = combinedCondition.back().value || paramCondition[k + 1].value; prevCondition = xOR; } else if (combinedCondition.back().op != xOR){ // Start a new combinedCondition tempCombinedCondition.value = paramCondition[k].value || paramCondition[k + 1].value; tempCombinedCondition.op = xOR; combinedCondition.push_back(tempCombinedCondition); prevCondition = xOR; } } } // Since the k + 1 value is included in the xAND or xOR exclusively, skip checking that value, and add the appropriate AND / OR as the // operator of this exclusive xAND / xOR set if ((paramCondition[k + 1].op == AND || paramCondition[k + 1].op == OR) && (prevCondition == xAND || prevCondition == xOR)){ combinedCondition.back().op = paramCondition[k + 1].op; k++; } } } // the last value was not included in any combination, since directly before the last value was an xAND / xOR set that // included the very last AND / OR as the set's operator, yet there is still another value that has not been combined, as it is supposed // to be AND /OR to the exclusive xAND / xOR set else if (last && (prevCondition == xAND || prevCondition == xOR) && (combinedCondition.back().op == AND || combinedCondition.back().op == OR)){ tempCombinedCondition.value = paramCondition[k].value; tempCombinedCondition.op = NONE; combinedCondition.push_back(tempCombinedCondition); } //reset the tempCombinedCondition variable tempCombinedCondition = ConditionOp(); } // run through all values in the combined Condition vector, AND'ing / OR'ing as appropriate // in the end, the last value in the array should be the final condition of the Condition statement... whether it was successful or failed for (i = 0; i < (combinedCondition.size() - 1); i++){ if (combinedCondition[i].op == AND) combinedCondition[i + 1].value = combinedCondition[i].value && combinedCondition[i + 1].value; else if (combinedCondition[i].op == OR) combinedCondition[i + 1].value = combinedCondition[i].value || combinedCondition[i + 1].value; } int conditionSuccess = combinedCondition.back().value; //value is the success(1) or failure(0) of the condition statement int checkEnd = 0, returnValue; if (!conditionSuccess){ while (checkEnd != 4){ returnValue = getNextLine(selectedFile, lineData); //if getNextLine returned an error, then error out if (returnValue == -1) return -1; // check if the first word is an end command (avoids interpreting functions that perform actions) if (lineData.word[0].compare("end") == 0) checkEnd = interpretCommand(selectedFile, lineData); if (checkEnd == 4) // custom return value for this function return 0; //Function operated successfully but doesn't return a value else if (checkEnd == -1) //if interpretCommand returned an error, then error out return -1; } } // Return success as the function either met the condition and will continue from the next line, or // failed to meet the condition and ran through the lines inside the condition until "end condition" was found, therefore // the program will proceed from the line after the "end condition" line return 0; //Function operated successfully but doesn't return a value } /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ /************************** <FUNCTION: loopCommand> *****************************/ /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ int loopCommand(FILE *selectedFile, LineData &lineData){ //Get the Condition value for number of times to loop string loopCondition = lineData.word[1]; int loopConditionValue = 0; int numValuesFound = sscanf(loopCondition.c_str(), "%d", &loopConditionValue); if (numValuesFound < 1){ ErrorOut("Parameter Unknown, loopCondition Value can't be converted", lineData.lineNumber); return -1; } int loopStartAddress = 0, loopLineNumber = 0, firstLineOfLoop = 1; lcd.setAddress(0,0); lcd.printf("Cycle 1 of %d", loopConditionValue); float totalLoopTime = 0; Timer cycleTimer; cycleTimer.reset(); cycleTimer.start(); int counter = 1, checkEnd = 0, returnValue; while (counter <= loopConditionValue){ returnValue = getNextLine(selectedFile, lineData); //if getNextLine returned an error, then return error out if (returnValue == -1) return -1; //Must get the address before entering the interpret command // if a Condition command is immediately after, and the condition fails, then // the interpret command will return the line at the "end condition" line, and therefore // set the loop's first line to be the "end condition" line, if interpretCommand is called BEFORE setting the first loop line address if (firstLineOfLoop){ loopStartAddress = lineData.lineAddress; //Save the Line Address loopLineNumber = lineData.lineNumber; //Save the Line Number firstLineOfLoop = 0; } checkEnd = interpretCommand(selectedFile, lineData); //Increase the loop counter and go back to the beginning of the loop if (checkEnd == 3){ //Output the Avg Cycle Time cycleTimer.stop(); totalLoopTime += cycleTimer.read(); lcd.setAddress(0,1); lcd.printf("Avg t(sec): %1.3f", (totalLoopTime / counter)); //Output Cycle Number counter++; lcd.setAddress(0,0); lcd.printf("Cycle %d of %d", counter, loopConditionValue); int seekFailure = fseek(selectedFile, loopStartAddress, SEEK_SET); //fseek returns 0 on success if (seekFailure){ ErrorOut("In Loop Failed to seek line", lineData.lineNumber); //Spaces make it look nice on the LCD return -1; } lineData.lineNumber = loopLineNumber - 1; checkEnd = 0; //Restart the timer for the next loop cycleTimer.reset(); cycleTimer.start(); } else if (checkEnd == -1) //if interpretCommand returned an error, then return error out return -1; } return 0; //Return Success, no value is being sent so don't return 1 } /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ /************************* <FUNCTION: interpretCommand> *************************/ /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ int interpretCommand(FILE *selectedFile, LineData &lineData){ if (lineData.word[0].compare("device") == 0){ int i = 0, deviceFound = -1; for (i = 0; i < numDevices; i++){ if (lineData.word[2].compare(DeviceNames[i]) == 0){ deviceFound = i; } } //if the device type does not match any known type, error out if (deviceFound == -1){ ErrorOut("No device match found in the system", lineData.lineNumber); //Spaces make it look nice on LCD return -1; } //Add device to the array of devices and initialize it else{ devices.push_back(Device::newDevice(deviceFound, lineData.word[1], lineData)); devices.back()->name = lineData.word[1]; //since the constructor cannot return a value, it will trip the error Flag if something is wrong, check that flag, and return error if it has been tripped if (devices.back()->errorFlag == 1){ ErrorOut("Error initializing device", lineData.lineNumber); return -1; } } } else if (lineData.word[0].compare("delay") == 0){ string duration = lineData.word[1]; int durationValue = 0; int numValuesFound = sscanf(duration.c_str(), "%d", &durationValue); if (numValuesFound < 1){ ErrorOut("Parameter Unknown, Duration Value can't be converted", lineData.lineNumber); return -1; } if (durationValue > 0){ timer.reset(); timer.start(); while (timer.read_ms() < durationValue); //Do Nothing while the timer has not reached the duration timer.stop(); //Stop the Timer } else{ ErrorOut("Duration value is less than 0", lineData.lineNumber); return -1; } } else if (lineData.word[0].compare("loop") == 0) return loopCommand(selectedFile, lineData); //Process the loop command and return the value that it returns else if (lineData.word[0].compare("condition") == 0) return conditionCommand(selectedFile, lineData); //Process the condition command and return the value that it returns //end has custom return value for specific functions, since "end" is a common keyword amongst functions else if (lineData.word[0].compare("end") == 0){ if (lineData.word[1].compare("program") == 0) return 2; else if (lineData.word[1].compare("loop") == 0) return 3; else if (lineData.word[1].compare("condition") == 0) return 4; else{ ErrorOut("Unknown function ending", lineData.lineNumber); return -1; } } //not a keyword so check if it's a localName for a device else{ int i = 0, deviceFound = -1; for (i = 0; i < devices.size(); i++){ if (lineData.word[0].compare(devices[i]->name) == 0) deviceFound = i; } //no device was found that matched the local name, and this is also the last error check, meaning it can match no other potential keywords if (deviceFound == -1){ ErrorOut("No device match found in the system", lineData.lineNumber); //Spaces make it look nice on LCD return -1; } //Local Name matches a device, send line to that device in order to process the functionality else return devices[deviceFound]->interpret(lineData); //call the device specific interpret command, and return the value it returns } return 0; //Return Success, no value is being sent so don't return 1 } /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ /****************************** <FUNCTION: main> ********************************/ /**********************************************************************************************************************************/ /**********************************************************************************************************************************/ int main() { fullInit(); //Initialize anything that's required to run the code (LCD) LineData lineData; resetLineData(lineData); /******************************************************************************/ /*** <Get all the Potential Programs> ***/ /******************************************************************************/ int numTextFiles = 0; vector<string> textFiles; //Assuming Maximum of 25 txt files will be on the SD Card vector<string> filenames = readFileNames("/sd"); //Error check whether the SD Card exists and was able to be accessed.... or if there's no files on the SD Card if (filenames.size() == 0){ ErrorOut("No Files Found, or Directory can't be accessed", 0); //Spaces make it look nice on LCD return -1; //End program by returning in the main() } numTextFiles = getFileNamesWithoutExt(textFiles, filenames); //Error check whether the SD Card has any txt files in it's first directory if (numTextFiles == 0){ ErrorOut("No Program (.txt) Files Found in first Directory", 0); //Spaces make it look nice on LCD return -1; //End program by returning in the main() } /******************************************************************************/ /*** <Select the Program txt File> ***/ /******************************************************************************/ int fileSelected = 0, selectedFileIndex = 0; lcd.cls(); //clear the display lcd.setAddress(0,1); lcd.printf("Select Your Program"); lcd.setAddress(0,2); lcd.printf("Num Programs = %d", numTextFiles); uint8_t lastButState = 0; lcd.setCursor(TextLCD::CurOn_BlkOn); //turn blinking cursor on selectedFileIndex = cyclePrograms(textFiles, numTextFiles, selectedFileIndex, -1); //Initialize the first file to the screen while(!fileSelected) { uint8_t curButState = buttons.readBus(); if(curButState != lastButState){ lastButState = curButState; if(buttons.readRight()) selectedFileIndex = cyclePrograms(textFiles, numTextFiles, selectedFileIndex, 1); else if(buttons.readLeft()) selectedFileIndex = cyclePrograms(textFiles, numTextFiles, selectedFileIndex, 0); else if(buttons.readSel()) fileSelected = 1; } } lcd.setCursor(TextLCD::CurOn_BlkOff); //turn blinking cursor off char selectedFileName[50]; strcpy(selectedFileName, textFiles[selectedFileIndex].c_str()); /******************************************************************************/ /*** <Open the Program txt File> ***/ /******************************************************************************/ //Create the string of the full directory and path to the program txt file char selectedFileFullName[100] = "/sd/"; //Assuming that no directory and file name will be longer than 100 characters strcat(selectedFileFullName, selectedFileName); strcat(selectedFileFullName, ".txt"); FILE *selectedFile = fopen(selectedFileFullName, "r"); //Error out of attempt to open the selected file was unsuccessful if(selectedFile == NULL) { ErrorOut("Unable to Open Selected File", 0); //Spaces make it look nice on LCD return -1; //End program by returning in the main() } /******************************************************************************/ /*** <Start Running through the Program txt File Lines> ***/ /******************************************************************************/ while(1){ resetLineData(lineData); //Reset the values in the struct that holds the Line Data, in preparation for a new line read lcd.cls(); //clear the display int endOfFile = 0, error = 0, returnValue, checkEnd; while (!endOfFile){ //Re-initialize variables returnValue = 0; checkEnd = 0; returnValue = getNextLine(selectedFile, lineData); //get the next line of data //if getNextLine returned an error, then return error in main if (returnValue == -1) error = 1; checkEnd = interpretCommand(selectedFile, lineData); //interpret the line data if (checkEnd == 2) endOfFile = 1; else if (checkEnd == -1) //if interpretCommand returned an error, then return error in main error = 1; //Before erroring out, turn all devices off so that they power down if (error){ for(vector<Device*>::iterator it=devices.begin(); it < devices.end(); it++) (*it)->off(); return -1; } } lcd.cls(); //clear the display lcd.setAddress(0,0); lcd.printf("END OF PROGRAM"); lcd.setAddress(0,2); lcd.printf("To Restart..."); lcd.setAddress(0,3); lcd.printf("Press BACK"); while(!buttons.readBack()); lcd.setAddress(0,1); //rewind(selectedFile); } }