Microcontroller firmware that uses a simple, yet powerful scripting language to control the timing of input and output events with high temporal resolution. Written by Mattias Karlsson

Dependencies:   SMARTWAV mbed

behave.h

Committer:
mkarlsso
Date:
2017-02-07
Revision:
8:872b843a3053
Parent:
7:5fe7329751d4

File content as of revision 8:872b843a3053:

//#include "mbed.h"
#include <stdint.h>
#include <string.h>
#include <string>
#include <vector>
#include <list>
#include <deque>
#include <queue>
#include "hardwareInterface.h"


#define MBEDHARDWARE  //here is where we define which platform we are compiling for
//#define FPGAHARDWARE

#define MAXVARNAMESIZE 30 //The maximum number of characters of a variable name

#ifdef MBEDHARDWARE
    #include "mbedInterface.h"
#endif
#ifdef FPGAHARDWARE
    #include "fpgaInterface.h"
#endif

#define BLOCKMEMORYTYPES 1
#define VARIABLEMEMORYTYPES 2
#define ENVSETTINGSMEMORYTYPES 4


/*
#define NUMEVENTS 50
#define NUMCONDITIONS 150
#define NUMINTCOMPARE 150
#define NUMACTIONS 150
#define NUMPORTMESSAGES 150
#define NUMINTOPERATIONS 150
#define NUMDISPLAYACTIONS 30
#define NUMTRIGGERACTIONS 30
#define NUMFUNCTIONS 50
#define INPUTCHARBUFFERSIZE 3072
*/

#define ARITHMATIC_CONDITION    0
#define OR_CONDITION    1
#define AND_CONDITION    2



using namespace std;

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

struct VersionInfo {
    int major;
    int middle;
    int minor;

    int updateDay;
    int updateMonth;
    int updateYear;
};

class AbstractPort {

public:
    AbstractPort();

    enum Direction{in,out,none};
    enum DataType{digital, analog};

    Direction portDir;
    DataType portType;

    virtual void write(int outVal);
    virtual int  read() = 0; //Must be defined in inheriting class
    virtual void setThresh(int threshVal);

    uint32_t lastChangeTime;
    uint32_t lastOutChangeTime;

    bool update(); //called from the main loop
    void setTriggerUpEvent(event* eventInput); //attahces a routine to an upward change
    void setTriggerDownEvent(event* eventInput); //attahces a routine to a downward change
    void clearTriggerEvents();
    void setReportUpdates(bool report);
    int getLastChangeState();
    uint32_t getTimeSinceLastChange();

    int state;
    bool outStateChanged;

    event* triggerUpEventPtr;
    event* triggerDownEventPtr;

protected:
    virtual bool checkForChange();
    bool hasTriggerFunction;
    bool reportUpdates;

    int lastInState;
    int thresh;
    uint32_t lastChangeInterval;

private:

};

class AnalogPort: public AbstractPort {
public:
    AnalogPort();

    void init(sAnalogIn* IP);
    void init(sAnalogOut* OP);

    void set(int outVal);
    int  read();
    void setThresh(int threshVal);
    void write(int outVal);

private:
    sAnalogOut* outPin;
    sAnalogIn*  inPin;

};

class DigitalPort: public AbstractPort {
public:
    DigitalPort();

    void init(sDigitalIn* IP);
    void init(sDigitalOut* OP);

    void set(int outVal);
    int  read();
    void write(int outVal);

protected:
    bool checkForChange();

private:
    sDigitalOut* outPin;
    sDigitalIn*  inPin;

};


//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();
    //digitalPort(sDigitalOut* DOP, sDigitalIn* DIP);
    void init(sDigitalOut* DOP, sDigitalIn* 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:

    sDigitalOut* outPin;
    sDigitalIn*  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(std::string& tagInput, int initialValue);
    void set(std::string& tagInput, int initialValue);
    int value;
    //string tag;
    char tag[MAXVARNAMESIZE+1];
    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();
    void set(int* variable, string varNameInput);
    void set(string text);
    bool isUsed;
    void execute();
    void release();

private:
    int* dVariable;
    string dText;

};

class triggerFunctionAction {

public:
    triggerFunctionAction();
    triggerFunctionAction(int functionNum);
    void set(int functionNum);
    bool isUsed;
    void execute();
    void release();
private:
    int functionNum;


};

//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();

    //Supported operations with rand:
    //a = rand(x)
    //a = rand(x) + 3
    //a = rand(x) + b
    void setRandOp(int randParam, const char* cmpString, int cmpValInput, bool flipped);
    void setRandOp(int randParam, const char* cmpString, int* cmpIntVarInput, bool flipped);

    //Supported regular operations
    //a = 5
    //a = b
    //a = b + 6
    //a = b + c
    void set(int* intVarInput, const char* cmpString, int cmpValInput);
    void set(int* intVarInput, const char* cmpString, int* cmpIntVarInput);
    void set(int* intVarInput, intOperation* operationInput);


    //Supported operations with clock()
    //a = clock()
    //a = clock() + 5
    //a = clock() + b
    void setClockOp(int* intVarInput);
    void setClockOp(const char* cmpString, int cmpValInput, bool flip);
    void setClockOp(const char* cmpString, int* cmpIntVarInput, bool flip);

    void release();
    bool isUsed;
    int execute();

private:
    int randHigh;
    int* cmpVal;
    int* intVal;
    intOperation* opPtr;
    bool cmpValGlobal;
    bool isClockAssign; //if the current clock value is part of the operation
    bool inputsFlipped;
    int (intOperation::*executePtr)();
    int addAndStore();
    int subtractAndStore();
    int add();
    int subtract();
    int equals();

};

//portMessage is an action to change a port state.
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, AbstractPort** portVector, int portVectorLength); //whichToSet:

    void execute();
    void release();
    bool isUsed;

private:
    int whichToSet; //hard coded port number
    int* port; //alternative variable port number
    int value;
    int vectorLength;

    AbstractPort** portVector;

};

//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(sSound* soundInput);
    action(triggerFunctionAction* triggerFuncInput);
    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(sSound* soundInput);
    void set(triggerFunctionAction* triggerFuncInput);
    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;
    sSound* sound;
    triggerFunctionAction* triggerFunc;
    //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(AbstractPort* portInput, const char* cmpString, int cmpValInput, int whichToUse);
    intCompare(AbstractPort* 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(AbstractPort* portInput, const char* cmpString, intOperation* cmpIntOpInput, int whichToUse);

    void set(AbstractPort* portInput, const char* cmpString, int cmpValInput, int whichToUse);
    void set(AbstractPort* 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(AbstractPort* portInput, const char* cmpString, intOperation* cmpIntOpInput, int whichToUse);

    void release();

    ~intCompare();
    bool isTrue();
    bool isUsed;

private:
    AbstractPort* 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();
    void addEventToQueue(event* eventInput, uint32_t delay);
    void eraseQueue(); //clear all future events
    void check(void);

private:
    std::vector<queueItem> events;
    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.  Not currently in use.
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:
#ifdef MBEDHARDWARE
    //On the MBED, we need to put this on a different memory bank
    __attribute((section("AHBSRAM1"),aligned)) char charBuffer[INPUTCHARBUFFERSIZE];
#else
    char charBuffer[INPUTCHARBUFFERSIZE];
#endif
    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(AbstractPort** portVectorInput, int numPortsInput, eventQueue* queueInput, sSystem* system);
    void parseBlock(); //Parses everything since the last semicolon was found
    void addLineToCurrentBlock(char* lineInput); // if the line did not end with a semicolon, add it to the current block

private:

    int* findIntVariable(string nameInput); //used to retrieve the pointer to the designated variable if it exists
    int* findIntVariable(const char* nameInput); //used to retrieve the pointer to the designated variable if it exists
    int* findIntVariable(const char* nameInput, int start, int end); //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)
    action* evaluateAssignmentForAction(const char* 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(const char* expression,int start, int end); //parses a condition statement (a == b && c > d)
    int findFirstOrOutsideParenth(const char* expression, int start, int end);
    int findFirstAndOutsideParenth(const char* expression, int start, int end);
    bool isOutsideParenth(const char* expression,int foundItem, int start, int end);
    bool isNum(const char* expression, int start, int end);
    bool areStringsSame(const char* str1, const char* str2, int start, int end);
    int getRandomParam(string expression);
    int getRandomParam(const char* expression,int start,int end);
    int findStringLoc(const char* refString,const char* findString,int start, int end);
    void clearEnvironmentVariables(int mode);

    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;
    bool expectingDoStatement;
    int currentDelay;
    event* tmpEvent;
    string tmpString;

    vector<intVariable*> globalVariables;
    vector<event*> tmpEventPtrArray;
    //event* functionEventArray[NUMFUNCTIONS];
    //bool   functionSpotTaken[NUMFUNCTIONS];

    //vector<functionItem*> functionArray; //any blocks declared outsite callback blocks are stored here
    //list<string> currentBlock;
    blockBuffer currentBlock;
    sSystem* system;

    AbstractPort** portVector;
    int numPorts;

    AbstractPort* digInPortVector[NUMDIGINPORTS];
    uint8_t digInLookup[NUMDIGINPORTS];
    int numDigInPorts;

    AbstractPort* digOutPortVector[NUMDIGOUTPORTS];
    uint8_t digOutLookup[NUMDIGOUTPORTS];
    int numDigOutPorts;

    AbstractPort* anInPortVector[NUMANINPORTS];
    uint8_t anInLookup[NUMANINPORTS];
    int numAnInPorts;

    AbstractPort* anOutPortVector[NUMANOUTPORTS];
    uint8_t anOutLookup[NUMANOUTPORTS];
    int numAnOutPorts;

    eventQueue* queuePtr;

};


class mainLoop

{
public:
    mainLoop();
    void init();
    void exec();
private:
    void eraseBuffer();
    uint32_t currentDIOstate[2];
    bool digitalInChanged;
    bool digitalOutChanged;
    scriptStream *parser;
    sSystem *hardware; //hardware interface
    sSerialPort *pc; //communication to computer
    char buffer[256];
    //digitalPort ports[NUMPORTS];

    DigitalPort digInPorts[NUMDIGINPORTS];
    DigitalPort digOutPorts[NUMDIGOUTPORTS];
    AnalogPort anInPorts[NUMANINPORTS];
    AnalogPort anOutPorts[NUMANOUTPORTS];

    AbstractPort* ports[NUMDIGINPORTS+NUMDIGOUTPORTS+NUMANINPORTS+NUMANOUTPORTS];

};