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