Interprets Commands received char by char.
CMD_Interpreter.cpp
- Committer:
- sepp_nepp
- Date:
- 2019-10-31
- Revision:
- 2:5934744ac614
- Parent:
- 0:b4341838304c
File content as of revision 2:5934744ac614:
// *****************************************************************
// Handle all the LEDs with some general function wrappers functions
// *****************************************************************
#include "CMD_Interpreter.h"
//************************************
// Interpreter Class Creation
//************************************
// Strategy: the interpreter accumulates characters in its input buffer
// it flags the presence of complete lines
// it also flags Overflow of the buffer in which case all subsequent characters
// are lost until the queue is emptied, and a CR-LF is received
// Defined States of the interpreter:
// MyState = isStartNew, isLineFilling, isOverflow, isWaitNewLine};
// #define DEBUG(...) { snprintf(buff, sizeof(buff), __VA_ARGS__); DataLog( buff ); }
Interpreter::Interpreter( void )
{ Reinit( );
MyState = isStartNew;
AllCommands = NULL;
NumCommands = 0;
InvalidCommand = false;
}
void Interpreter::FillCommands( int aNumCommands, const EXE_CMD_TYPE *ACommands )
{ int cnt;
AllCommands = new EXE_CMD_TYPE[ aNumCommands ];
for( cnt=0; cnt<aNumCommands; cnt++)
{ AllCommands[cnt] = ACommands[cnt]; } ;
NumCommands = aNumCommands;
}
void Interpreter::Reinit( void ) {
MyState = isWaitNewLine; // indicates no buffer overflow
WriteIndex = 0; // points to next index to write to
LinesComplete = 0; // indicates that no complete line is currently available
// Start Scanning at the start
ScanIndex = 0;
BufAvail = BuffLen;// the full buffer size is available
}
// Barebone function, assumes that checks havee been performed by writeBuf!
void Interpreter::AddChar( char aChar ) {
if (aChar == 8) // is it a backspace code, then remove one character:
{ if (BufAvail<BuffLen) { // there's actually a character to be removed
BufAvail++; // Buffer regrows
if (WriteIndex == 0) {WriteIndex=BuffLen-1;}
else {WriteIndex--;} // recoil the write index
}
}
else {
if (WriteIndex == BuffLen-1) {WriteIndex=0;}
else {WriteIndex++;}
RingBuf[WriteIndex]=aChar; // all right, buffer it!
BufAvail--; // Buffer is shrinking
if (BufAvail==0) { MyState = isOverflow; }
}
}
// High level method to add a Char to the buffer,
// Separates at Chars 10, 13, 0; replaced by a single 0 char
// Blocking write when buffer has overflowed
void Interpreter::writeBuf(char aChar) {
bool LineEnds = aChar==10 || aChar==13 || aChar==0;
switch (MyState) {
case isOverflow: break;
case isStartNew: // ready for the next line to start
// avoid that consecutive CR LF are counted as multiple lines
if (!LineEnds) {AddChar(aChar ); MyState = isLineFilling; }
break;
case isLineFilling:// New line has started filling
if (!LineEnds) {AddChar(aChar);}
else
{MyState = isStartNew; // ready for the next line
// Between consecutive commands, the endstring=NULL character is inserted
// this is to indicate that line was already counted as completed
AddChar(0 ); // append a line end char, will detect bufferFull!
LinesComplete++; } // count completed lines
break;
case isWaitNewLine: // waiting for a new line end to arrive after an overflow
if (LineEnds) { MyState = isStartNew; }
break;
default: MyState = isOverflow; // goes into error state, should never happen though
}
} // writeBuf
// Barebone function, that performs the checks to be performed!
// Passes back the actC, and reads already the nextChar into actC
char Interpreter::GetAChar( void ) {
char oldC = actC;
if (BufAvail==BuffLen) { actC = 0; LinesComplete=0; } // buffer is empty, no more lines!!
else // something is in the buffer
{ if (ScanIndex == BuffLen) {ScanIndex=0;}
else {ScanIndex++;}
actC=RingBuf[ScanIndex]; // all right, get it
if (actC==0) {LinesComplete--; } // now there is one line less in the storage
BufAvail++; // Buffer is increasing
} // something is in the buffer
return oldC;
}
// skip true blank, but also Tab characters
void Interpreter::SkipBlanks( void ) {
while(BufAvail<BuffLen && (actC==' ' || actC==9|| actC==0)) { GetAChar(); }
} // SkipBlanks
int Interpreter::ReadAnInt( void ) {
bool Negative = false;
int Result = 0;
if (actC=='-') {Negative = true; GetAChar(); }
else if (actC=='+') {Negative = false; GetAChar(); }
while(BufAvail<BuffLen && actC>='0' && actC<='9')
{ Result = Result * 10 + (GetAChar()-'0') ; }
return Negative? -Result : Result;
} // ReadAnInt
RD_CMD_TYPE Interpreter::ParseCommand( void ) {
RD_CMD_TYPE cmd; // locally built command
actC=RingBuf[ScanIndex]; // initialiye the actC variable, as the first char to use
SkipBlanks();
// Next Character is the command
cmd.Command = GetAChar();
// Next Blanks are to be omitted, but are not even mandatory
SkipBlanks();
if ((actC>='0' && actC<='9') || actC=='-' || actC=='+' )
{ cmd.Parameter= ReadAnInt(); cmd.NumParam = 1; }
else { cmd.Parameter= 0; cmd.NumParam = 0; }
SkipBlanks( ); // There should be at least a trailing NUL character to be removed
return cmd; // return the built command
}
bool Interpreter::executeCommand(RD_CMD_TYPE cmd) {
int CmdNr;
bool Found;
CmdNr = 0;
Found = false;
// DEBUG("Com '%c'=%d %d NPar=%d \n\r",cmd.Command, cmd.Command, cmd.Parameter, cmd.NumParam);
// While not found go through all the commands, linear; they are not sorted by alphabet.
while(CmdNr<NumCommands && !Found)
{ // DEBUG("NR:%d \n\r", CmdNr);
if (AllCommands[CmdNr].cmd == cmd.Command)
{ // the command character matches
if (cmd.NumParam == AllCommands[CmdNr].Npar)
{ // DEBUG("Execute: %s \n\r", AllCommands[CmdNr].help);
AllCommands[CmdNr].Handler( cmd.Parameter ); // then call the handler
}
// else { DataLog("Param Mismatch!\n\r"); } // Inform about Parameter error
Found = true; // and mark as found
}
else { CmdNr++; } // go on serarching
} // while
if ( !Found ) { // CmdInval(cmd.Command) ; // call invalid command function
InvalidCommand = true;
}
return Found;
}
bool Interpreter::ProcessPresentCommands( void )
{ RD_CMD_TYPE Readcmd;
if (MyState==isOverflow) { Reinit(); }
if(LinesComplete>0)
{ // DEBUG("X: Scan=%d ", Interp.ScanIndex);
Readcmd = ParseCommand();
executeCommand( Readcmd );
//DEBUG("state=%d; wix=%d; rix=%d; buf=%s ", Interp.MyState, Interp.WriteIndex, Interp.ScanIndex, &Interp.RingBuf[Interp.ScanIndex]);
return true;
}
else { return false; }
}
char *ParamHelp[] = {" ", "<param>", "<par><par>"};
void Interpreter::PrtCmdHelp (Serial *aSerial ) // Display list of Commands
{ EXE_CMD_TYPE *com;
int CmdNr;
char *parStr;
// DataLog("List of commands: \n\r");
// Go Through all commands in the table
for (CmdNr = 0; CmdNr<NumCommands; CmdNr++ )
{ com = &AllCommands[CmdNr]; // get a pointer to the command
parStr = ParamHelp[com->Npar]; // Get the string that represents the number of parameters
aSerial->printf( " %c %s => %s \n\r", com->cmd, parStr, com->help.c_str() ); // output the full command help
}
}
/*
void Interpreter::CmdInval( int param ) // Feedback that the command is invalid
{ DEBUG( " Invalid command %d = '%c'; use: 'h' for help. \n\r", param, char(param) ) ;
}
string Interpreter::GetLastMessage( void )
{ string LastMsg = LastMessage;
LastMessage = ""; // Empty the old Message
return LastMsg;
}
void Interpreter::DataLog( char *DebugMessage )
{ if (LastMessage=="" ) { LastMessage = DebugMessage; }
else { LastMessage = LastMessage + "\n\r" + DebugMessage; }
};
*/