| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- #define SCPI_CORE_STATE__PROCESSDATA_GUARD
- #include "app/scpi/CommandParserStates/process_data.h"
- #define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
- #define SCPI_PROCESS_DATA_C // access to some const variables in 'scpi_core_private.h'
- #include "app/scpi/scpi_core_private.h"
- #include "app/scpi/scpi_args.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"
- // =================================================================================
- // @sProcessProgramDataContext_t
- // State's private context typedef
- typedef struct
- {
- // @seqCommandProcessor
- // The SCPI command functor-sequencer
- xFSeqObj_t seqCommandProcessor;
- // @commonCtx
- // Common context for all command handlers
- sProcessProgramDataCommonContext_t commonCtx;
- }
- sProcessProgramDataContext_t;
- // =================================================================================
- // @ctx_ProcessProgramData
- // State's private context
- sProcessProgramDataContext_t ctx_ProcessProgramData;
- // =================================================================================
- // @fsqvbl_ProcessProgramData
- // State's virtual table
- static void fsqe_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
- static void fsql_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
- static const struct fFSeqEntry_t * fsqf_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx, const struct fFSeqEntry_t * * pDeferredNext );
- const fFSeqVTable_t fsqvbl_ProcessProgramData =
- {
- .f = fsqf_ProcessProgramData,
- .enter = fsqe_ProcessProgramData,
- .leave = fsql_ProcessProgramData
- };
- // =================================================================================
- // @fsqe_ProcessProgramData
- // State's enter routine
- static void fsqe_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
- {
- sScpiParserContext_t * global_ctx = ctx;
- sProcessProgramDataContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
- assert ( eScpiEventWrite == global_ctx->sEvent.eCode
- || eScpiEventRead == global_ctx->sEvent.eCode );
-
- // pass through the global context reference
- private_ctx->commonCtx.global_ctx = global_ctx;
- // pass through the handler context reference
- private_ctx->commonCtx.handler_ctx = global_ctx->sParser.xHandler->ctx;
- // forward set status-indicator
- private_ctx->commonCtx.status = eProgramDataSyntaxError;
- // reset arguments count
- private_ctx->commonCtx.args = 0;
- private_ctx->commonCtx.needParseEndOfCommand = true; // forward set
- private_ctx->commonCtx.argsParserStatus = eScpiStatus_invalid; // forward set
- private_ctx->commonCtx.event = eProgramData_Event_Write; // forward event code for handler
- // construct state machine object, pass through the global context reference
- fSeqConstruct( &private_ctx->seqCommandProcessor,
- &global_ctx->sParser.xHandler->entry,
- &private_ctx->commonCtx );
- // startup the SCPI parser state machine
- fSeqStartup( &private_ctx->seqCommandProcessor, NULL );
- // check if the command handler already set a correct parser
- // iterator to the end of current command: if not, it is required
- // search for end of the command to search the next in the line.
- if( private_ctx->commonCtx.needParseEndOfCommand )
- {
- parseArguments_helper( &private_ctx->commonCtx, NULL, 0, 0 );
- }
- }
- // =================================================================================
- // @fsql_ProcessProgramData
- // State's leave routine
- static void fsql_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
- {
- sScpiParserContext_t * global_ctx = ctx;
- sProcessProgramDataContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
- // shutdown the SCPI parser state machine
- fSeqShutdown( &private_ctx->seqCommandProcessor );
- (void)global_ctx;
- }
- // =================================================================================
- // @fsqf_ProcessProgramData
- // State's body routine
- //
- // Note: processes 'read' and 'write' events for SCPI command data payload
- //
- static const struct fFSeqEntry_t * fsqf_ProcessProgramData( const struct fFSeqEntry_t * this,
- tFSeqCtx_t ctx,
- const struct fFSeqEntry_t * * pnext )
- {
- const struct fFSeqEntry_t * nextstate = NULL;
- sScpiParserContext_t * global_ctx = ctx;
- sProcessProgramDataContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
-
- // Process event:
- switch( global_ctx->sEvent.eCode )
- {
- case eScpiEventRestart:
- {
- // go to reset state
- nextstate = fsq_GetStateById(this,eParserResetState);
- goto L_fsqf_ProcessProgramData_EXIT;
- }
- break;
- case eScpiEventError: // Transport error.
- {
- // Enter error state
- nextstate = fsq_GetStateById(this,eHandleError); // enter the error state
- goto L_fsqf_ProcessProgramData_EXIT;
- }
- break;
- case eScpiEventWrite: // this dispatch() is called directly or after @eProcessCommandHeader
- my_assert( private_ctx->commonCtx.event == eProgramData_Event_Write );
- case eScpiEventRead: // this dispatch() is called directly or after @eProcessCommandHeader
- {
- // Place response message separator
- // See [1], 8.4.1 <RESPONSE MESSAGE UNIT SEPARATOR>
- //
- if( global_ctx->sMessage.bLeadResponseSep )
- {
- // By design the @bLeadResponseSep is set only after the previous
- // executed command in the chain. If there no free room in the output
- // buffer, the command handler can not place it's own response.
- // But if there free room in the buffer the first element is placed must
- // be response message unit separator.
- if( global_ctx->sRead.nBufferSize > 0 )
- {
- // Place response unit separator before calling command handler
- scpi_WriteCharOutput( SCPI_CHARACTER_RESPONSE_SEPARATOR );
- global_ctx->sMessage.bLeadResponseSep = false; // reset indicator
- }
- else
- {
- // keep indicator for the next iteration
- }
- }
- }
- break;
- default: // invalid event code
- {
- // Enter error state
- nextstate = fsq_GetStateById(this,eHandleError); // enter the error state
- goto L_fsqf_ProcessProgramData_EXIT;
- }
- }
- assert( eProgramData_Event_Done != private_ctx->commonCtx.event );
- private_ctx->commonCtx.status = eProgramDataSyntaxError; // forward set
- private_ctx->commonCtx.isQuery = global_ctx->sMessage.bQuery;
- private_ctx->commonCtx.argErrIdx = SCPI_MAX_ARGS; // reset erroneous argument index to default
- // process the internal state machine state
- // Note: @private_ctx->commonCtx.event is firstly prepared in @fsqe_ProcessProgramData
- fSeqDispatch( &private_ctx->seqCommandProcessor );
- switch( private_ctx->commonCtx.status )
- {
- case eProgramDataNeedRead: // Check for: output-buffer filled
- {
- // set global status: 'success'
- global_ctx->sEvent.eStatus = eScpiStatus_success;
- // need to continue reading, it is not done yet.
- nextstate = NULL; // stay in current state
- // Check if OUTPUT buffer is available (eScpiEventRead):
- switch( global_ctx->sEvent.eCode )
- {
- // Output buffer is available
- case eScpiEventRead:
- {
- // "6.1.10.2.1 Message Available Message (MAV)", [1]
- scpi_UpdateMessageAvailable();
- // Check actual handler event code:
- switch( private_ctx->commonCtx.event )
- {
- // Handler just have processed 'Write' event
- case eProgramData_Event_Write:
- {
- // Handler returns NEED READ during WRITE event
- // Change event code for handler
- private_ctx->commonCtx.event = eProgramData_Event_Read;
- // And call this state again now to provide 'Reading' for the handler
- nextstate = this;
- }
- break;
- // Handler just have processed 'Read' event
- case eProgramData_Event_Read:
- {
- // Handler returns NEED READ during READ event
- // in case EOM (end-of-messsage) indicator has been reset by handler
- if( ! global_ctx->sMessage.r_bEndOfMessage )
- {
- // return 'eScpiStatus_need_data' status instead of 'eScpiStatus_success'
- // This provides another call to read the rest part of data
- global_ctx->sEvent.eStatus = eScpiStatus_need_data;
- }
- }
- break;
- default: assert(false); // impossible code
- }
- }
- break;
- // No output buffer is available
- case eScpiEventWrite:
- {
- // "6.1.10.2.1 Message Available Message (MAV)", [1]
- // .sRead structure is not initialized, but the handler respond that
- // is there data to send.
- // Force Set MAV bit
- // "11.5.2.1 Function", [1]
- // "<...> MAV becomes TRUE whenever data is ready and not necessarily in the Output Queue; <...>"
- GPIBMachine.fGPIB_set_message_available_bit( &global_ctx->sGPIB.registers, true );
- // Handler status is 'eProgramDataNeedRead', so it is required to change next event code.
- // Change event code for handler
- private_ctx->commonCtx.event = eProgramData_Event_Read;
- }
- break;
- }
- }
- break;
- case eProgramDataNeedData: // Check for: need data
- {
- // more data required
- // do not leave the state, keep @eScpiStatus_need_data status
- global_ctx->sEvent.eStatus = eScpiStatus_need_data;
- (void)private_ctx->commonCtx.event; // keep event code unchagned
- }
- break;
- case eProgramDataDone: // Check for: done-indicator
- {
- // Command completely done.
-
- // Check event code
- switch( global_ctx->sEvent.eCode )
- {
- // "6.1.10.2.1 Message Available Message (MAV)", [1]
- // .sRead structure is not initialized in *some* cases
- case eScpiEventWrite:
- case eScpiEventRead:
- {
- global_ctx->sMessage.r_bEndOfMessage = true; // assert read-event end-of-message indicator
- // "6.1.10.2.1 Message Available Message (MAV)", [1]
- scpi_UpdateMessageAvailable();
- }
- break;
- }
- // Fix: USBTMC does not allow empty transfer (transfer size equal to 0)
- // So, it is requried to send at least one byte if no response is available
- // after success code is returned.
- if( eScpiEventRead == global_ctx->sEvent.eCode )
- if( 0 == global_ctx->sRead.nTotalLength ) // transfer length
- if( 0 < global_ctx->sRead.nBufferSize )
- {
- scpi_WriteCharOutput( ' ' );
- }
- private_ctx->commonCtx.event = eProgramData_Event_Done; // formal reset event code to 'Done'
- // set success status
- global_ctx->sEvent.eStatus = eScpiStatus_success;
-
- // Search for the following message unit separator
- // goto next state: eProcessEndOfProgramData
- nextstate = fsq_GetStateById(this,eProcessEndOfProgramData);
- }
- break;
- default: // error cases
- {
- // Specify the argument token to the last parsed argument token
- const sStrToken_t * pErroneousArgumentToken = &global_ctx->sParser.xArgToken;
- // Check if the erroneous argument index is set
- if( private_ctx->commonCtx.argErrIdx < SCPI_MAX_ARGS )
- // Check if index is in range of parsed arguments count
- if( private_ctx->commonCtx.argErrIdx < private_ctx->commonCtx.args )
- {
- // Handler has specified the particularly parsed token as erroneous
- pErroneousArgumentToken = &private_ctx->commonCtx.argTokens[ private_ctx->commonCtx.argErrIdx ];
- }
- switch( private_ctx->commonCtx.status )
- {
- case eProgramDataRuntimeError: // Check for: run-time error
- {
- // Generate error message: common execution error
- // "11.5.1.1.5 Bit 4 N Execution ERROR (E)", [2]
- // "<...> the device shall continue parsing the input stream. <...>"
- fsq_RaiseError( SCPI_ERROR_EXECUTION_ERROR, SCPI_ERROR_EXECUTION_ERROR_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );
- // Note: Execution error shall not cause the parser stop operate,
- // so, the error handler must process error code and pass the control to the
- // 'endof_process_data' state to continue processing next command.
- }
- break;
- case eProgramDataSyntaxError: // Check for: syntax error
- {
- // Generate error message
- fsq_RaiseError( SCPI_ERROR_SYNTAX_ERROR, SCPI_ERROR_SYNTAX_ERROR_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );
- }
- break;
- case eProgramDataArgumentSyntax: // Check for: argument syntax error
- {
- // Command handler reports about argument parsing error
- // The specific error is already prepared in parser context
- // Retrieve error details:
- int32_t error;
- const char * pErrorMsg = getParserError( &(global_ctx->sParser.xCtxObj), &error );
-
- if( error != SCPI_ERROR_SUCCESS )
- {
- const void * shead;
- const void * stail;
- getParsedEntityDetails( &(global_ctx->sParser.xCtxObj), &shead, &stail, NULL );
-
- // Generate error message
- fsq_RaiseErrorEx( error, pErrorMsg,
- shead, stail, // character
- global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail // command
- );
- }
- }
- break;
- case eProgramDataIllegalArgument: // Check for: illegal argument value
- {
- // Command handler reports about illegal argument value
- // Generate error message
- fsq_RaiseErrorEx( SCPI_ERROR_ILLEGAL_PARAM, SCPI_ERROR_ILLEGAL_PARAM_MSG,
- pErroneousArgumentToken->shead, pErroneousArgumentToken->stail, // character
- global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail // command
- );
- }
- break;
- case eProgramDataArgumentType: // Check for: argument type error
- case eProgramDataArgumentValue: // Check for: argument value error
- {
- // Command handler reports about argument value/range error
- // The specific error must be generated here
- // Generate error message
- fsq_RaiseErrorEx( SCPI_ERROR_PARAMETER_ERROR, SCPI_ERROR_PARAMETER_ERROR_MSG,
- pErroneousArgumentToken->shead, pErroneousArgumentToken->stail, // character
- global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail // command
- );
- }
- break;
- case eProgramDataArgumentRange: // Check for: argument value range error
- {
- // Command handler reports about argument range error
- // The specific error must be generated here
- // Generate error message
- fsq_RaiseErrorEx( SCPI_ERROR_DATA_RANGE, SCPI_ERROR_DATA_RANGE_MSG,
- pErroneousArgumentToken->shead, pErroneousArgumentToken->stail, // character
- global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail // command
- );
- }
- break;
- case eProgramData_SpecificError: // Check for: specific error
- {
- (void)fsq_RaiseError; // specific error message already generated by handler
- }
- break;
- default:
- {
- // Generate error message (common error code)
- fsq_RaiseError( SCPI_ERROR_COMMAND_ERROR, SCPI_ERROR_COMMAND_ERROR_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );
- }
- }
- // formal reset event code to 'Done'
- private_ctx->commonCtx.event = eProgramData_Event_Done;
- // set failed status: see @fsql_ProcessProgramData
- global_ctx->sEvent.eStatus = eScpiStatus_failed;
-
- // Error condition
- // goto next state: eHandleError
- nextstate = fsq_GetStateById(this,eHandleError);
- }
- break;
- }
-
- L_fsqf_ProcessProgramData_EXIT:
- return nextstate;
- }
|