Auxiliaries I use for CreaBot
Diff: CMD_Interpreter.cpp
- Revision:
- 1:6f5b84940d04
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMD_Interpreter.cpp Fri Jun 21 15:26:49 2019 +0000 @@ -0,0 +1,200 @@ +// ***************************************************************** +// 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; +} + +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 + 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; } +}; +*/