![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
A scripting environment used to define precise output/input temporal relationships.
Dependencies: SMARTWAV mbed HelloWorld
Dependents: perturbRoom_legacy
Fork of HelloWorld by
behave.h
- Committer:
- mkarlsso
- Date:
- 2014-09-25
- Revision:
- 4:34aca2142df9
- Parent:
- 2:298679fff37c
File content as of revision 4:34aca2142df9:
#include "mbed.h" #include <stdint.h> #include <string.h> #include <string> #include <vector> #include <list> #include <deque> #include <queue> #include "soundControl.h" #define NUMEVENTS 50 #define NUMCONDITIONS 150 #define NUMINTCOMPARE 150 #define NUMACTIONS 150 #define NUMPORTMESSAGES 150 #define NUMINTOPERATIONS 150 #define NUMDISPLAYACTIONS 30 #define ARITHMATIC_CONDITION 0 #define OR_CONDITION 1 #define AND_CONDITION 2 #define NUMPORTS 8 #define INPUTCHARBUFFERSIZE 3072 class event; //we foreward declare this because of class interdependencies //used in the digital port class to organize digital change events struct changeEvent { uint32_t timeStamp; bool triggered; }; //The digitalPort object directly controls and keeps data about the port. Each port has //one digital out and one digital in. class digitalPort { public: digitalPort(DigitalOut* DOP, DigitalIn* DIP); void setDigitalOut(int outVal); //int getDigitalOut(); int getDigitalIn(); int getLastChangeState(); uint32_t getTimeSinceLastChange(); uint32_t lastChangeTime; uint32_t lastOutChangeTime; void setTriggerUpEvent(event* eventInput); //attahces a routine to an upward change void setTriggerDownEvent(event* eventInput); //attahces a routine to a downward change void addStateChange(int newState, uint32_t timeStamp); bool update(); //called from the main loop int inState; int outState; bool outStateChanged; event* triggerUpEventPtr; event* triggerDownEventPtr; private: DigitalOut* outPin; DigitalIn* inPin; int lastInState; uint32_t lastChangeInterval; changeEvent lastUpEvent; changeEvent lastDownEvent; }; //an intVariable contains an integer value and the name of that variable within the script class intVariable { public: intVariable(); intVariable(string tagInput, int initialValue); void set(string tagInput, int initialValue); int value; string tag; bool isUsed; }; //ACTION SECTION-- an 'action' is a command in the script. It can be a single command, //or a block containing a set of actions //------------------------------------------------------------------------------------ //display actions are used to output text messages via the serial port. The user can display //either a static text string or the value of a single variable. class displayAction { public: displayAction(); displayAction(int* variable, string varNameInput, Serial* pcPtrInput); displayAction(string text, Serial* pcPtrInput); void set(int* variable, string varNameInput, Serial* pcPtrInput); void set(string text, Serial* pcPtrInput); bool isUsed; void execute(); void release(); private: int* dVariable; string dText; Serial* pcPtr; }; //intOpertaion is an action that does addition or subtraction of integers and returns/stores the result //these operation are very limited so far (only + or - allowed, and only one operation per object, //for example a = b + b works but a = b + c + d does not. The output value can also be set to a random number. class intOperation { public: intOperation(); intOperation(int randParam, const char* cmpString, int cmpValInput); intOperation(int randParam, const char* cmpString, int* cmpIntVarInput); intOperation(int* intVarInput, const char* cmpString, int cmpValInput); intOperation(int* intVarInput, const char* cmpString, int* cmpIntVarInput); intOperation(int* intVarInput, intOperation* operationInput); ~intOperation(); void set(int randParam, const char* cmpString, int cmpValInput); void set(int randParam, const char* cmpString, int* cmpIntVarInput); void set(int* intVarInput, const char* cmpString, int cmpValInput); void set(int* intVarInput, const char* cmpString, int* cmpIntVarInput); void set(int* intVarInput, intOperation* operationInput); void release(); bool isUsed; int execute(); private: int randHigh; int* cmpVal; int* intVal; intOperation* opPtr; bool cmpValGlobal; int (intOperation::*executePtr)(); int addAndStore(); int subtractAndStore(); int add(); int subtract(); int equals(); }; //portMessage is an action to change a digital port. So far, You can only change the digital out (0 or 1) class portMessage { public: portMessage(); //portMessage(digitalPort* portIn, int whichToSet, int value); //whichToSet: 1 DigitalOut; 2 State //void setMessage(digitalPort* portIn, int whichToSet, int value); //whichToSet: 1 DigitalOut; 2 State portMessage(int* portIn, int whichToSet, int value); //whichToSet: void setMessage(int* portIn, int whichToSet, int value); //whichToSet: void execute(); void release(); bool isUsed; private: int whichToSet; //hard coded port number int* port; //alternative variable port number int value; //digitalPort* port; }; //holder class for all possible actions. This include general system commands. class action { public: action(); ~action(); action(intOperation* opInput); action(portMessage* messageInput); action(event* eventInput); //action(event* eventInput, uint32_t delay); action(displayAction* displayInput); action(soundControl* soundInput); action(int8_t sysCommandInput); //for general system commands void set(intOperation* opInput); void set(portMessage* messageInput); void set(event* eventInput); //void set(event* eventInput, uint32_t delay); void set(displayAction* displayInput); void set(soundControl* soundInput); void set(int8_t sysCommandInput); void execute(); void execute(uint32_t blockExecTime); void release(); bool isUsed; private: intOperation* op; portMessage* message; event* eventToCreate; displayAction* displayActionPtr; soundControl* sound; //uint32_t eventDelay; int8_t sysCommand; char actionType; }; //----------------------------------------------------- //CONDITION SECTION-- a 'condition' is used in the beginning of a block (if-else blocks or while blocks) //If the condition is true, the block is exectuted during a callback //------------------------------------------------------------------------------------ //intCompare is a condition class that compares the state value of a port or //an integer variable to another integer variable or operation output class intCompare { public: intCompare(); intCompare(digitalPort* portInput, const char* cmpString, int cmpValInput, int whichToUse); intCompare(digitalPort* portInput, const char* cmpString, int* cmpIntVarInput, int whichToUse); intCompare(int* intVarInput, const char* cmpString, int cmpValInput); intCompare(int* intVarInput, const char* cmpString, int* cmpIntVarInput); intCompare(int* intVarInput, const char* cmpString, intOperation* cmpIntOpInput); intCompare(digitalPort* portInput, const char* cmpString, intOperation* cmpIntOpInput, int whichToUse); void set(digitalPort* portInput, const char* cmpString, int cmpValInput, int whichToUse); void set(digitalPort* portInput, const char* cmpString, int* cmpIntVarInput, int whichToUse); void set(int* intVarInput, const char* cmpString, int cmpValInput); void set(int* intVarInput, const char* cmpString, int* cmpIntVarInput); void set(int* intVarInput, const char* cmpString, intOperation* cmpIntOpInput); void set(digitalPort* portInput, const char* cmpString, intOperation* cmpIntOpInput, int whichToUse); void release(); ~intCompare(); bool isTrue(); bool isUsed; private: digitalPort* port; int* portValPtr; int* cmpVal; int* intVal; intOperation* intOp; void setPointer(const char* cmpString); void setPointer_operation(const char* cmpString); bool (intCompare::*isTruePtr)(); bool cmpValGlobal; bool greaterThan(); bool greaterOrEqual(); bool lessThan(); bool lessOrEqual(); bool equal(); bool notEqual(); bool greaterThan_op(); bool greaterOrEqual_op(); bool lessThan_op(); bool lessOrEqual_op(); bool equal_op(); bool notEqual_op(); }; //holder class for all possible conditions (so far only intCompare) class condition { public: condition(); condition(intCompare* compareInput); condition(condition* condition1, char condType, condition* condition2); ~condition(); void set(intCompare* compareInput); void set(condition* condition1, char condType, condition* condition2); bool isTrue(); bool isUsed; void release(); //called when the event is no longer being used; private: //char conditionType; //1 for intCompare intCompare* intCmp; condition* conditionPtrs[2]; char conditionType; }; //-------------------------------------------- //queueItem connects a pre-defined event with an exectution time. //They are placed in the eventQueue struct queueItem { uint32_t timeToExecute; event* eventPtr; }; //Organizes events in a temporal queue. check() is called from the main loop. //If the execution time of the event has passed, then the event is exectuted. class eventQueue { public: eventQueue(digitalPort** portVectorInput, uint32_t* timeKeeperSlaveInput); void addEventToQueue(event* eventInput, uint32_t delay); void eraseQueue(); //clear all future events void check(void); private: std::vector<queueItem> events; digitalPort** portVector; uint32_t* timeKeeperPtr; int queueSize; }; //An 'event' is a block of 'actions' that can be gated with a boolean 'condition' set. All //conditions in the set must be true for the block of actions to be executed. Right now, //there is no OR logic (||), only AND (&&). //The entire event is placed on the event queue to be executed at a given delay. //At that future time, the condition is checked and if true, the block of actions //is exectuted. Note: an 'action' can be another event (or even the parent event), allowing //nested 'if' and 'while' statements. class event { public: event(); event(eventQueue* queueInput); ~event(); void setTimeLag(uint32_t timeLagInput); //the event will be exectuted at this time from now void setTimeLag(int* timeLagInput); //the event will be exectuted at this time from now void setWhileLoopPeriod(uint32_t period); void setWhileLoopPeriod(int* period); void addCondition(condition* conditionInput); //contains a set of conditions to check and a truth table bool isConditionTrue(void); //checks if the condition is true void addAction(action* actionInput); //called during script parsing, when the block is being defined void addToQueue(void); //places the event on the event queue with default time lag. When the time //lag has expired, the the block is executed void addToQueue(uint32_t delay); void execute(void); //Execute without checking the condition. Called only from the event queue void setNextElseEvent(event* eventInput); //allows for else event block uint32_t timeLag; //default time from now when the event will be executed (this is ignored once placed in event queue) int* timeLagVar; //exectution time lab defined by a variable eventQueue* queuePtr; void release(); //called when the event is no longer being used; char blockType; //0 callback //1 if ... do block (with conditions) //2 do block (no conditions) //3 else if ... do block //4 else do (no conditions) //5 while ... do every ... block //6 else while ... do every ... block //7 then if ... do block //8 then do (no conditions) uint32_t whileLoopPeriod; //if non-zero, the block is a while loop (executed at regular intervals) int* whileLoopPeriodVar; event* nextElseEventPtr; bool isUsed; bool timeLagIsConstant; bool whileLoopPeriodIsConstant; bool hasWhileLoop; private: int numConditions; int numActions; condition* conditionToCheck; action* actionArray[20]; //if statement (can be left empty, which is interpreted as 'true') //std::vector<condition*> conditionArray; //std::deque<action*> actionArray; }; //each functionItem help a poiter to an action, and the name of the function class functionItem { public: functionItem(action* actionInput, string tagInput); ~functionItem(); string tag; action* actionPtr; }; class blockBuffer { public: blockBuffer(); bool addLine(char* input, int numChars); string getNextLine(); int16_t linesAvailable(); bool empty(); void resetBuffer(); private: //__attribute((section("AHBSRAM1"),aligned)) char charBuffer[INPUTCHARBUFFERSIZE]; char charBuffer[INPUTCHARBUFFERSIZE]; int16_t bufferWritePos; int16_t bufferReadPos; int16_t _linesAvailable; }; //Parser for the incoming text. The parser is called when a line terminates with a semicolon (;). //Only the final line in a callback block should have a semicolon. class scriptStream { public: scriptStream(Serial* serialInput, digitalPort** portVectorInput, int numPortsInput, eventQueue* queueInput); void parseBlock(); void addLineToCurrentBlock(char* lineInput); // if the line did not end with a semicolon, add it to the current block int* findIntVariable(string nameInput); //used to retrieve the pointer to the designated variable if it exists bool createIntVariable(string nameInput); // creates a new interger variable action* evaluateAssignmentForAction(string expression); //parses a numerical assignment or operation (a = b - c) bool evaluateConditions(string expression, event* currentEvent); //parses a condition statement (a == b && c > d) condition* parseConditions(string expression); //parses a condition statement (a == b && c > d) std::size_t findFirstOrOutsideParenth(string expression); std::size_t findFirstAndOutsideParenth(string expression); bool isOutsideParenth(string expression,std::size_t foundItem); int getRandomParam(string expression); private: int currentTriggerPort; int currentTriggerDir; int currentPort; int currentFunction; string tmpLine; vector<string> tokens; bool lineError; int blockDepth; bool ifBlockInit; bool whileBlockInit; bool elseFlag; bool thenFlag; int currentDelay; event* tmpEvent; string tmpString; vector<intVariable*> globalVariables; vector<event*> tmpEventPtrArray; vector<functionItem*> functionArray; //any blocks declared outsite callback blocks are stored here //list<string> currentBlock; blockBuffer currentBlock; digitalPort** portVector; int numPorts; Serial* pcPtr; eventQueue* queuePtr; }; //Used to buffer output text. Used mainly for 'display' commands within the script, //and alloed the reset of the block to execute quickly instead. The text is then streamed out //slowly via serial (one character per main loop execution). outputStream is a simple circular //buffer that cannot be resized after initiation. class outputStream { public: outputStream(int bufferSizeIn); ~outputStream(); void send(string outputString); char getNextChar(); bool unsentData; private: int readHead; int writeHead; int totalWriteHead; int totalReadHead; int bufferSize; char tmpOut; char* outputBuffer; };