#define SCPI_CORE_STATE__PROCESSCOMMAND_GUARD #include "app/scpi/CommandParserStates/process_command.h" #define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h' #define SCPI_PROCESS_COMMAND_C // access to some const variables in 'scpi_core_private.h' #include "app/scpi/scpi_core_private.h" #include "app/scpi/scpi_commands.h" //---------------------------------------------------------------- // Refer to: // [1] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 (1992) // "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE // Standard Digital Interface for Programmable Instrumentation" // [2] SCPI Specification, revision 1999.0 // "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999" // [3] Gpib Programming Tutorial, (http://g2pc1.bu.edu/~qzpeng/gpib/manual/GpibProgTut.pdf) // Electronics Group (http://www.few.vu.nl/~elec), 11 January 2000 Electronics Group //---------------------------------------------------------------- // ================================================================================= // @sProcessCommandHeaderContext_t // State's private context typedef typedef struct { uint8_t eCmdHeader_type; bool bKeepParserContext; } sProcessCommandHeaderContext_t; // ================================================================================= // @ctx_ProcessCommandHeader // State's private context sProcessCommandHeaderContext_t ctx_ProcessCommandHeader; // ================================================================================= // @fsqvbl_ProcessCommandHeader // State's virtual table static void fsqe_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx ); static void fsql_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx ); static const struct fFSeqEntry_t * fsqf_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx, const struct fFSeqEntry_t * * pDeferredNext ); const fFSeqVTable_t fsqvbl_ProcessCommandHeader = { .f = fsqf_ProcessCommandHeader, .enter = fsqe_ProcessCommandHeader, .leave = fsql_ProcessCommandHeader }; // ================================================================================= // @fsqe_ProcessCommandHeader // State's enter routine static void fsqe_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx ) { sScpiParserContext_t * global_ctx = ctx; sProcessCommandHeaderContext_t * private_ctx = fsq_GetPrivateCtxRef(this); assert( private_ctx ); assert( global_ctx ); const char * pCmdHeader_shead = NULL; const char * pCmdHeader_stail = NULL; private_ctx->bKeepParserContext = false; // reset parser context at next call // @eCmdHeader_type: need to identify query/command (@eScpiEntityType_t) private_ctx->eCmdHeader_type = eScpiEntityTypeInvalid; // Retrieve the command header entity info: getParsedEntityDetails( &(global_ctx->sParser.xCtxObj), (void const**)&pCmdHeader_shead, (void const**)&pCmdHeader_stail, &private_ctx->eCmdHeader_type ); assert( pCmdHeader_shead ); assert( pCmdHeader_stail ); assert( eScpiEntityTypeInvalid != private_ctx->eCmdHeader_type ); // update current input buffer iterator global_ctx->sMessage.pStr = (const uint8_t*)pCmdHeader_stail; // search for command handler // [3], "3.4 Omitting the header path", // This first header is assumed to be found in the root branch. So, it is required to save // the branch if the command is found. global_ctx->sParser.xHandler = scpiSearchCommandHandler( (const sTreeList_t **)&global_ctx->sParser.xCmdBranch, pCmdHeader_shead, pCmdHeader_stail, private_ctx->eCmdHeader_type, &global_ctx->sParser.xHandlerToken, global_ctx->sParser.bFirstCommandIndicator ); // reset the first command indicator global_ctx->sParser.bFirstCommandIndicator = false; if( NULL == global_ctx->sParser.xHandler ) { global_ctx->sParser.xHandlerToken.shead = pCmdHeader_shead; global_ctx->sParser.xHandlerToken.stail = pCmdHeader_stail; // Enter error state fsq_RaiseError( SCPI_ERROR_UNDEFHEADER_ERROR, SCPI_ERROR_UNDEFHEADER_ERROR_MSG, pCmdHeader_shead, pCmdHeader_stail ); } } // ================================================================================= // @fsql_ProcessCommandHeader // State's leave routine static void fsql_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx ) { } // ================================================================================= // @fsqf_ProcessCommandHeader // State's body routine static const struct fFSeqEntry_t * fsqf_ProcessCommandHeader( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx, const struct fFSeqEntry_t * * pnext ) { sScpiParserContext_t * global_ctx = ctx; sProcessCommandHeaderContext_t * private_ctx = fsq_GetPrivateCtxRef(this); const struct fFSeqEntry_t * nextstate = fsq_GetStateById(this,eHandleError); // go next state: eHandleError by default assert( private_ctx ); assert( global_ctx ); // Formal checking if( eScpiEventRestart == global_ctx->sEvent.eCode ) { // go to reset state nextstate = fsq_GetStateById(this,eParserResetState); goto L_fsqf_ProcessCommandHeader_EXIT; } if( eScpiEventError == global_ctx->sEvent.eCode ) // Transport error. { // Enter error state nextstate = fsq_GetStateById(this,eHandleError); // enter the error state goto L_fsqf_ProcessCommandHeader_EXIT; } // check if has the handler been found: if( NULL == global_ctx->sParser.xHandler ) { global_ctx->sEvent.eStatus = eScpiStatus_failed; // Note: impossible to complete parsing the rest part of the message // due to the data stage of the undefinded command can not be recognized. return nextstate; // Note: go to next state: eHandleError (default) } // It is required to skip Command Program Header separator ("7.4.3, [1]"): { // Retrieve the command header entity info to get the tail pointer if the last parsed entity: const char * shead = (const char *)global_ctx->sMessage.pStr; // determine the new tail pointer (@sWrite.nDataLength may change each call) const char * stail = (const char *)global_ctx->sMessage.pEnd; assert( shead <= stail ); // In current step it is required to find command separator: header separator or message terminator. // If a command have a data parameter, the parser will find header separator (space), otherwise // the parser will find either message terminator (NL) or message separator (semicolon). // Declare a set of parser functions to use: eScpiParserStatus_t (* x_fParser[2])( xParseEntry_t * xObj ) = { parseProgramHeaderSeparator, // try to find program header separator (HEADER DATA) parseProgramMessageSeparator // try to find program message separator (HEADER1 HEADER2) }; size_t iParserIdx = 0; // current parser index L_fsqf_ProcessCommandHeader_again: // marker to return // prepare parser context if( !prepareParserContext( &(global_ctx->sParser.xCtxObj), // re-use @xCtxObj object shead, // pass input buffer (stail - shead), // pass input buffer size global_ctx->sMessage.w_bEndOfMessage, // pass end-of-message by identifying the event private_ctx->bKeepParserContext ) ) // parser context keep indicator { global_ctx->sEvent.eStatus = eScpiStatus_invalid; (void)nextstate; // Note: go to next state: eHandleError (default) } else { global_ctx->sEvent.eStatus = eScpiStatus_failed; // forward set // try to call the parser only if index is not out of range if( iParserIdx < (sizeof(x_fParser)/sizeof(*x_fParser)) ) { // 1. iParserIdx=0: parseProgramHeaderSeparator // 2. iParserIdx=1: parseProgramMessageSeparator global_ctx->sEvent.eStatus = x_fParser[iParserIdx++]( &(global_ctx->sParser.xCtxObj) ); } // check status: switch( global_ctx->sEvent.eStatus ) { // in case success: case eScpiStatus_success: { // successfully parsed: program command header separator // Retrieve the last parsed entity tail and // ... update current input buffer iterator @sMessage.pStr getParsedEntityDetails( &(global_ctx->sParser.xCtxObj), NULL, (void const**)&global_ctx->sMessage.pStr, NULL ); (void)global_ctx->sMessage.pStr; assert( eScpiEventWrite == global_ctx->sEvent.eCode || eScpiEventRead == global_ctx->sEvent.eCode ); assert( eScpiEntityTypeCmnQueryProgHdr == private_ctx->eCmdHeader_type || eScpiEntityTypeCmpQueryProgHdr == private_ctx->eCmdHeader_type || eScpiEntityTypeCmnCommandProgHdr == private_ctx->eCmdHeader_type || eScpiEntityTypeCmpCommandProgHdr == private_ctx->eCmdHeader_type ); bool eventProcess = false; // decode entity type to encode new event for next state switch( private_ctx->eCmdHeader_type ) { // For command-type entities: case eScpiEntityTypeCmnCommandProgHdr: case eScpiEntityTypeCmpCommandProgHdr: { global_ctx->sMessage.bQuery = false; // Command type eventProcess = true; } break; // For query-type entities: case eScpiEntityTypeCmnQueryProgHdr: case eScpiEntityTypeCmpQueryProgHdr: { global_ctx->sMessage.bQuery = true; // Query type eventProcess = true; } break; default: // impossible eventProcess = false; } if( eventProcess ) { // Command mnemonic successfully parsed // The command is ready to be executed // Restore parsing iterator to search command parameters global_ctx->sMessage.pStr = (const uint8_t*)global_ctx->sParser.xHandlerToken.stail; // prepare parser context if( !prepareParserContext( &(global_ctx->sParser.xCtxObj), global_ctx->sMessage.pStr, // pass input buffer (ptrdiff_t)global_ctx->sMessage.pEnd - (ptrdiff_t)global_ctx->sMessage.pStr, // pass input buffer size global_ctx->sMessage.w_bEndOfMessage, // pass end-of-message by identifying the event private_ctx->bKeepParserContext ) ) { assert(false); // forbidden state global_ctx->sEvent.eStatus = eScpiStatus_failed; // Something went wrong... fsq_RaiseError( SCPI_ERROR_EXECUTION_ERROR, SCPI_ERROR_EXECUTION_ERROR_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail ); (void)nextstate; // Note: go to next state: eHandleError (default) } else { // Next station: data processing state, be careful, the state is about closing :) // go to next state: eProcessProgramData nextstate = fsq_GetStateById(this,eProcessProgramData); } } else { global_ctx->sEvent.eStatus = eScpiStatus_failed; assert(false); // forbidden state // Something went wrong... fsq_RaiseError( SCPI_ERROR_EXECUTION_ERROR, SCPI_ERROR_EXECUTION_ERROR_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail ); (void)nextstate; // Note: go to next state: eHandleError (default) } } break; case eScpiStatus_need_data: { private_ctx->bKeepParserContext = true; // keep parser context til next call nextstate = NULL; // NULL, do not change state } break; case eScpiStatus_failed: case eScpiStatus_invalid: default: { // try another parser if( iParserIdx < (sizeof(x_fParser)/sizeof(*x_fParser)) ) { goto L_fsqf_ProcessCommandHeader_again; } // Enter error state fsq_RaiseError( SCPI_ERROR_COMMAND_HEADER_SEP, SCPI_ERROR_COMMAND_HEADER_SEP_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail ); (void)nextstate; // Note: go to next state: eHandleError (default) } break; } } } L_fsqf_ProcessCommandHeader_EXIT: return nextstate; }