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