process_data.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. #define SCPI_CORE_STATE__PROCESSDATA_GUARD
  2. #include "app/scpi/CommandParserStates/process_data.h"
  3. #define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h'
  4. #define SCPI_PROCESS_DATA_C // access to some const variables in 'scpi_core_private.h'
  5. #include "app/scpi/scpi_core_private.h"
  6. #include "app/scpi/scpi_args.h"
  7. // Refer to:
  8. // [1] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 (1992)
  9. // "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE
  10. // Standard Digital Interface for Programmable Instrumentation"
  11. // [2] SCPI Specification, revision 1999.0
  12. // "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999"
  13. // =================================================================================
  14. // @sProcessProgramDataContext_t
  15. // State's private context typedef
  16. typedef struct
  17. {
  18. // @seqCommandProcessor
  19. // The SCPI command functor-sequencer
  20. xFSeqObj_t seqCommandProcessor;
  21. // @commonCtx
  22. // Common context for all command handlers
  23. sProcessProgramDataCommonContext_t commonCtx;
  24. }
  25. sProcessProgramDataContext_t;
  26. // =================================================================================
  27. // @ctx_ProcessProgramData
  28. // State's private context
  29. sProcessProgramDataContext_t ctx_ProcessProgramData;
  30. // =================================================================================
  31. // @fsqvbl_ProcessProgramData
  32. // State's virtual table
  33. static void fsqe_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
  34. static void fsql_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
  35. static const struct fFSeqEntry_t * fsqf_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx, const struct fFSeqEntry_t * * pDeferredNext );
  36. const fFSeqVTable_t fsqvbl_ProcessProgramData =
  37. {
  38. .f = fsqf_ProcessProgramData,
  39. .enter = fsqe_ProcessProgramData,
  40. .leave = fsql_ProcessProgramData
  41. };
  42. // =================================================================================
  43. // @fsqe_ProcessProgramData
  44. // State's enter routine
  45. static void fsqe_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
  46. {
  47. sScpiParserContext_t * global_ctx = ctx;
  48. sProcessProgramDataContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
  49. assert ( eScpiEventWrite == global_ctx->sEvent.eCode
  50. || eScpiEventRead == global_ctx->sEvent.eCode );
  51. // pass through the global context reference
  52. private_ctx->commonCtx.global_ctx = global_ctx;
  53. // pass through the handler context reference
  54. private_ctx->commonCtx.handler_ctx = global_ctx->sParser.xHandler->ctx;
  55. // forward set status-indicator
  56. private_ctx->commonCtx.status = eProgramDataSyntaxError;
  57. // reset arguments count
  58. private_ctx->commonCtx.args = 0;
  59. private_ctx->commonCtx.needParseEndOfCommand = true; // forward set
  60. private_ctx->commonCtx.argsParserStatus = eScpiStatus_invalid; // forward set
  61. private_ctx->commonCtx.event = eProgramData_Event_Write; // forward event code for handler
  62. // construct state machine object, pass through the global context reference
  63. fSeqConstruct( &private_ctx->seqCommandProcessor,
  64. &global_ctx->sParser.xHandler->entry,
  65. &private_ctx->commonCtx );
  66. // startup the SCPI parser state machine
  67. fSeqStartup( &private_ctx->seqCommandProcessor, NULL );
  68. // check if the command handler already set a correct parser
  69. // iterator to the end of current command: if not, it is required
  70. // search for end of the command to search the next in the line.
  71. if( private_ctx->commonCtx.needParseEndOfCommand )
  72. {
  73. parseArguments_helper( &private_ctx->commonCtx, NULL, 0, 0 );
  74. }
  75. }
  76. // =================================================================================
  77. // @fsql_ProcessProgramData
  78. // State's leave routine
  79. static void fsql_ProcessProgramData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
  80. {
  81. sScpiParserContext_t * global_ctx = ctx;
  82. sProcessProgramDataContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
  83. // shutdown the SCPI parser state machine
  84. fSeqShutdown( &private_ctx->seqCommandProcessor );
  85. (void)global_ctx;
  86. }
  87. // =================================================================================
  88. // @fsqf_ProcessProgramData
  89. // State's body routine
  90. //
  91. // Note: processes 'read' and 'write' events for SCPI command data payload
  92. //
  93. static const struct fFSeqEntry_t * fsqf_ProcessProgramData( const struct fFSeqEntry_t * this,
  94. tFSeqCtx_t ctx,
  95. const struct fFSeqEntry_t * * pnext )
  96. {
  97. const struct fFSeqEntry_t * nextstate = NULL;
  98. sScpiParserContext_t * global_ctx = ctx;
  99. sProcessProgramDataContext_t * private_ctx = fsq_GetPrivateCtxRef(this);
  100. // Process event:
  101. switch( global_ctx->sEvent.eCode )
  102. {
  103. case eScpiEventRestart:
  104. {
  105. // go to reset state
  106. nextstate = fsq_GetStateById(this,eParserResetState);
  107. goto L_fsqf_ProcessProgramData_EXIT;
  108. }
  109. break;
  110. case eScpiEventError: // Transport error.
  111. {
  112. // Enter error state
  113. nextstate = fsq_GetStateById(this,eHandleError); // enter the error state
  114. goto L_fsqf_ProcessProgramData_EXIT;
  115. }
  116. break;
  117. case eScpiEventWrite: // this dispatch() is called directly or after @eProcessCommandHeader
  118. my_assert( private_ctx->commonCtx.event == eProgramData_Event_Write );
  119. case eScpiEventRead: // this dispatch() is called directly or after @eProcessCommandHeader
  120. {
  121. // Place response message separator
  122. // See [1], 8.4.1 <RESPONSE MESSAGE UNIT SEPARATOR>
  123. //
  124. if( global_ctx->sMessage.bLeadResponseSep )
  125. {
  126. // By design the @bLeadResponseSep is set only after the previous
  127. // executed command in the chain. If there no free room in the output
  128. // buffer, the command handler can not place it's own response.
  129. // But if there free room in the buffer the first element is placed must
  130. // be response message unit separator.
  131. if( global_ctx->sRead.nBufferSize > 0 )
  132. {
  133. // Place response unit separator before calling command handler
  134. scpi_WriteCharOutput( SCPI_CHARACTER_RESPONSE_SEPARATOR );
  135. global_ctx->sMessage.bLeadResponseSep = false; // reset indicator
  136. }
  137. else
  138. {
  139. // keep indicator for the next iteration
  140. }
  141. }
  142. }
  143. break;
  144. default: // invalid event code
  145. {
  146. // Enter error state
  147. nextstate = fsq_GetStateById(this,eHandleError); // enter the error state
  148. goto L_fsqf_ProcessProgramData_EXIT;
  149. }
  150. }
  151. assert( eProgramData_Event_Done != private_ctx->commonCtx.event );
  152. private_ctx->commonCtx.status = eProgramDataSyntaxError; // forward set
  153. private_ctx->commonCtx.isQuery = global_ctx->sMessage.bQuery;
  154. private_ctx->commonCtx.argErrIdx = SCPI_MAX_ARGS; // reset erroneous argument index to default
  155. // process the internal state machine state
  156. // Note: @private_ctx->commonCtx.event is firstly prepared in @fsqe_ProcessProgramData
  157. fSeqDispatch( &private_ctx->seqCommandProcessor );
  158. switch( private_ctx->commonCtx.status )
  159. {
  160. case eProgramDataNeedRead: // Check for: output-buffer filled
  161. {
  162. // set global status: 'success'
  163. global_ctx->sEvent.eStatus = eScpiStatus_success;
  164. // need to continue reading, it is not done yet.
  165. nextstate = NULL; // stay in current state
  166. // Check if OUTPUT buffer is available (eScpiEventRead):
  167. switch( global_ctx->sEvent.eCode )
  168. {
  169. // Output buffer is available
  170. case eScpiEventRead:
  171. {
  172. // "6.1.10.2.1 Message Available Message (MAV)", [1]
  173. scpi_UpdateMessageAvailable();
  174. // Check actual handler event code:
  175. switch( private_ctx->commonCtx.event )
  176. {
  177. // Handler just have processed 'Write' event
  178. case eProgramData_Event_Write:
  179. {
  180. // Handler returns NEED READ during WRITE event
  181. // Change event code for handler
  182. private_ctx->commonCtx.event = eProgramData_Event_Read;
  183. // And call this state again now to provide 'Reading' for the handler
  184. nextstate = this;
  185. }
  186. break;
  187. // Handler just have processed 'Read' event
  188. case eProgramData_Event_Read:
  189. {
  190. // Handler returns NEED READ during READ event
  191. // in case EOM (end-of-messsage) indicator has been reset by handler
  192. if( ! global_ctx->sMessage.r_bEndOfMessage )
  193. {
  194. // return 'eScpiStatus_need_data' status instead of 'eScpiStatus_success'
  195. // This provides another call to read the rest part of data
  196. global_ctx->sEvent.eStatus = eScpiStatus_need_data;
  197. }
  198. }
  199. break;
  200. default: assert(false); // impossible code
  201. }
  202. }
  203. break;
  204. // No output buffer is available
  205. case eScpiEventWrite:
  206. {
  207. // "6.1.10.2.1 Message Available Message (MAV)", [1]
  208. // .sRead structure is not initialized, but the handler respond that
  209. // is there data to send.
  210. // Force Set MAV bit
  211. // "11.5.2.1 Function", [1]
  212. // "<...> MAV becomes TRUE whenever data is ready and not necessarily in the Output Queue; <...>"
  213. GPIBMachine.fGPIB_set_message_available_bit( &global_ctx->sGPIB.registers, true );
  214. // Handler status is 'eProgramDataNeedRead', so it is required to change next event code.
  215. // Change event code for handler
  216. private_ctx->commonCtx.event = eProgramData_Event_Read;
  217. }
  218. break;
  219. }
  220. }
  221. break;
  222. case eProgramDataNeedData: // Check for: need data
  223. {
  224. // more data required
  225. // do not leave the state, keep @eScpiStatus_need_data status
  226. global_ctx->sEvent.eStatus = eScpiStatus_need_data;
  227. (void)private_ctx->commonCtx.event; // keep event code unchagned
  228. }
  229. break;
  230. case eProgramDataDone: // Check for: done-indicator
  231. {
  232. // Command completely done.
  233. // Check event code
  234. switch( global_ctx->sEvent.eCode )
  235. {
  236. // "6.1.10.2.1 Message Available Message (MAV)", [1]
  237. // .sRead structure is not initialized in *some* cases
  238. case eScpiEventWrite:
  239. case eScpiEventRead:
  240. {
  241. global_ctx->sMessage.r_bEndOfMessage = true; // assert read-event end-of-message indicator
  242. // "6.1.10.2.1 Message Available Message (MAV)", [1]
  243. scpi_UpdateMessageAvailable();
  244. }
  245. break;
  246. }
  247. // Fix: USBTMC does not allow empty transfer (transfer size equal to 0)
  248. // So, it is requried to send at least one byte if no response is available
  249. // after success code is returned.
  250. if( eScpiEventRead == global_ctx->sEvent.eCode )
  251. if( 0 == global_ctx->sRead.nTotalLength ) // transfer length
  252. if( 0 < global_ctx->sRead.nBufferSize )
  253. {
  254. scpi_WriteCharOutput( ' ' );
  255. }
  256. private_ctx->commonCtx.event = eProgramData_Event_Done; // formal reset event code to 'Done'
  257. // set success status
  258. global_ctx->sEvent.eStatus = eScpiStatus_success;
  259. // Search for the following message unit separator
  260. // goto next state: eProcessEndOfProgramData
  261. nextstate = fsq_GetStateById(this,eProcessEndOfProgramData);
  262. }
  263. break;
  264. default: // error cases
  265. {
  266. // Specify the argument token to the last parsed argument token
  267. const sStrToken_t * pErroneousArgumentToken = &global_ctx->sParser.xArgToken;
  268. // Check if the erroneous argument index is set
  269. if( private_ctx->commonCtx.argErrIdx < SCPI_MAX_ARGS )
  270. // Check if index is in range of parsed arguments count
  271. if( private_ctx->commonCtx.argErrIdx < private_ctx->commonCtx.args )
  272. {
  273. // Handler has specified the particularly parsed token as erroneous
  274. pErroneousArgumentToken = &private_ctx->commonCtx.argTokens[ private_ctx->commonCtx.argErrIdx ];
  275. }
  276. switch( private_ctx->commonCtx.status )
  277. {
  278. case eProgramDataRuntimeError: // Check for: run-time error
  279. {
  280. // Generate error message: common execution error
  281. // "11.5.1.1.5 Bit 4 N Execution ERROR (E)", [2]
  282. // "<...> the device shall continue parsing the input stream. <...>"
  283. fsq_RaiseError( SCPI_ERROR_EXECUTION_ERROR, SCPI_ERROR_EXECUTION_ERROR_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );
  284. // Note: Execution error shall not cause the parser stop operate,
  285. // so, the error handler must process error code and pass the control to the
  286. // 'endof_process_data' state to continue processing next command.
  287. }
  288. break;
  289. case eProgramDataSyntaxError: // Check for: syntax error
  290. {
  291. // Generate error message
  292. fsq_RaiseError( SCPI_ERROR_SYNTAX_ERROR, SCPI_ERROR_SYNTAX_ERROR_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );
  293. }
  294. break;
  295. case eProgramDataArgumentSyntax: // Check for: argument syntax error
  296. {
  297. // Command handler reports about argument parsing error
  298. // The specific error is already prepared in parser context
  299. // Retrieve error details:
  300. int32_t error;
  301. const char * pErrorMsg = getParserError( &(global_ctx->sParser.xCtxObj), &error );
  302. if( error != SCPI_ERROR_SUCCESS )
  303. {
  304. const void * shead;
  305. const void * stail;
  306. getParsedEntityDetails( &(global_ctx->sParser.xCtxObj), &shead, &stail, NULL );
  307. // Generate error message
  308. fsq_RaiseErrorEx( error, pErrorMsg,
  309. shead, stail, // character
  310. global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail // command
  311. );
  312. }
  313. }
  314. break;
  315. case eProgramDataIllegalArgument: // Check for: illegal argument value
  316. {
  317. // Command handler reports about illegal argument value
  318. // Generate error message
  319. fsq_RaiseErrorEx( SCPI_ERROR_ILLEGAL_PARAM, SCPI_ERROR_ILLEGAL_PARAM_MSG,
  320. pErroneousArgumentToken->shead, pErroneousArgumentToken->stail, // character
  321. global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail // command
  322. );
  323. }
  324. break;
  325. case eProgramDataArgumentType: // Check for: argument type error
  326. case eProgramDataArgumentValue: // Check for: argument value error
  327. {
  328. // Command handler reports about argument value/range error
  329. // The specific error must be generated here
  330. // Generate error message
  331. fsq_RaiseErrorEx( SCPI_ERROR_PARAMETER_ERROR, SCPI_ERROR_PARAMETER_ERROR_MSG,
  332. pErroneousArgumentToken->shead, pErroneousArgumentToken->stail, // character
  333. global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail // command
  334. );
  335. }
  336. break;
  337. case eProgramDataArgumentRange: // Check for: argument value range error
  338. {
  339. // Command handler reports about argument range error
  340. // The specific error must be generated here
  341. // Generate error message
  342. fsq_RaiseErrorEx( SCPI_ERROR_DATA_RANGE, SCPI_ERROR_DATA_RANGE_MSG,
  343. pErroneousArgumentToken->shead, pErroneousArgumentToken->stail, // character
  344. global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail // command
  345. );
  346. }
  347. break;
  348. case eProgramData_SpecificError: // Check for: specific error
  349. {
  350. (void)fsq_RaiseError; // specific error message already generated by handler
  351. }
  352. break;
  353. default:
  354. {
  355. // Generate error message (common error code)
  356. fsq_RaiseError( SCPI_ERROR_COMMAND_ERROR, SCPI_ERROR_COMMAND_ERROR_MSG, global_ctx->sParser.xHandlerToken.shead, global_ctx->sParser.xHandlerToken.stail );
  357. }
  358. }
  359. // formal reset event code to 'Done'
  360. private_ctx->commonCtx.event = eProgramData_Event_Done;
  361. // set failed status: see @fsql_ProcessProgramData
  362. global_ctx->sEvent.eStatus = eScpiStatus_failed;
  363. // Error condition
  364. // goto next state: eHandleError
  365. nextstate = fsq_GetStateById(this,eHandleError);
  366. }
  367. break;
  368. }
  369. L_fsqf_ProcessProgramData_EXIT:
  370. return nextstate;
  371. }