Merge branch 'master' of https://github.com/j123b567/scpi-parser
28个文件已修改
1个文件已添加
1 文件已重命名
| | |
| | | SCPI parser library |
| | | =========== |
| | | |
| | | [SCPI](http://en.wikipedia.org/wiki/Standard_Commands_for_Programmable_Instruments) Parser library aims to provide parsing ability of SCPI commands on instrument side. All commands are defined by its patterns eg: "STATus:QUEStionable:EVENt?". |
| | | [SCPI](http://en.wikipedia.org/wiki/Standard_Commands_for_Programmable_Instruments) Parser library aims to provide parsing ability of SCPI commands on instrument side. All commands are defined by their patterns eg: "STATus:QUEStionable:EVENt?". |
| | | |
| | | Source codes are published with open source Simplified BSD license. |
| | | Source code is published with open source Simplified BSD license. |
| | | |
| | | Command pattern definition |
| | | ----------- |
| | | Command pattern is defined by well known representation from SCPI instruments. Pattern is case insensitive but uses lower and upper case letters to show short and long form of the command. |
| | | A command pattern is defined using the well known command representation from SCPI instruments. A pattern is case insensitive but uses lower and upper case letters to show the short and long form of the command. |
| | | |
| | | Pattern "SYSTem" matches strings "SYST", "syst", "SyStEm", "system", ... |
| | | |
| | | Command pattern is devided by colon ":" to show command hierarchy |
| | | A command pattern is divided by colon ":" to show command hierarchy |
| | | |
| | | Pattern "SYSTem:VERsion?" mathes strings "SYST:version?", "system:ver?", "SYST:VER?", ... |
| | | |
| | | SCPI standard also uses brackets "[]" to define unnecesery parts of command. This behaviour is not implemented yet. |
| | | The SCPI standard also uses brackets "[]" to define optional parts of a command. |
| | | |
| | | Pattern "SYSTem:ERRor[:NEXT]?" should match "SYST:ERR?", "system:err?" and also "system:error:next?", ... |
| | | |
| | | In current implementation, you should write two patterns to implement this behaviour |
| | | |
| | | Pattern "SYSTem:ERRor?" and "SYSTem:ERRor:NEXT?" |
| | | Pattern "SYSTem:ERRor[:NEXT]?" mathes "SYST:ERR?", "system:err?" and also "system:error:next?", ... |
| | | |
| | | |
| | | Command callback |
| | | ----------- |
| | | Command callbac is defined as function with context parameter, e.g.: |
| | | Command callback is defined as a function with a context parameter, e.g.: |
| | | |
| | | ```c |
| | | int DMM_MeasureVoltageDcQ(scpi_context_t * context) |
| | | ``` |
| | | |
| | | The "Q" at the end of the function name indicates, that this function is Query function (command with "?"). |
| | | The "Q" at the end of the function name indicates that this function is a Query function (command with "?"). |
| | | |
| | | The command callback can use predefined function to parse input parameters and to write output. |
| | | The command callback can use predefined functions to parse input parameters and to write output. |
| | | |
| | | Reading input parameter is done by functions `SCPI_ParamInt`, `SCPI_ParamDouble`, `SCPI_ParamString` adn `SCPI_ParamNumber`. |
| | | Reading input parameters is done by using the functions `SCPI_ParamInt`, `SCPI_ParamDouble`, `SCPI_ParamString` and `SCPI_ParamNumber`. |
| | | |
| | | Writing output is done by functions `SCPI_ResultInt`, `SCPI_ResultDouble`, `SCPI_ResultString`, `SCPI_ResultText`. You can write multiple output variables. They are automaticcaly separated by coma ",". |
| | | Writing output is done by using the functions `SCPI_ResultInt`, `SCPI_ResultDouble`, `SCPI_ResultString`, `SCPI_ResultText`. You can write multiple output variables. They are automaticcaly separated by comma ",". |
| | | |
| | | Source code organisation |
| | | ------------ |
| | | |
| | | Source codes are devided into few files to provide better portability to other systems. |
| | | Source codes are divided into a few files to provide better portability to other systems. |
| | | |
| | | - *libscpi/parser.c* - provides the core parser library |
| | | - *libscpi/error.c* - provides basic error handling (error queue of the instrument) |
| | | - *libscpi/ieee488.c* - provides basic implementation of IEEE488.2 mandatory commands |
| | | - *libscpi/minimal.c* - provides basic implementation of SCPI mandatory commands |
| | | - *libscpi/utils.c* - provides string handling routines and conversion routines |
| | | - *libscpi/units.c* - provides handling of special numners (DEF, MIN, MAX, ...) and units |
| | | - *libscpi/fifo.c* - provides basic implementation of error queue FIFO |
| | | - *libscpi/debug.c* - provides debug functions |
| | | - *libscpi/src/parser.c* - provides the core parser library |
| | | - *libscpi/src/error.c* - provides basic error handling (error queue of the instrument) |
| | | - *libscpi/src/ieee488.c* - provides basic implementation of IEEE488.2 mandatory commands |
| | | - *libscpi/src/minimal.c* - provides basic implementation of SCPI mandatory commands |
| | | - *libscpi/src/utils.c* - provides string handling routines and conversion routines |
| | | - *libscpi/src/units.c* - provides handling of special numners (DEF, MIN, MAX, ...) and units |
| | | - *libscpi/src/fifo.c* - provides basic implementation of error queue FIFO |
| | | - *libscpi/src/debug.c* - provides debug functions |
| | | |
| | | - *examples/test-parser* - is the basic non-interactive demo of the parser |
| | | - *examples/test-interactive* - is the basic interactive demo of the parser |
| | |
| | | |
| | | Implementation to your instrument |
| | | ------------- |
| | | First of all you need to fill structure of SCPI command definitions |
| | | First of all you need to fill the structure of SCPI command definitions |
| | | |
| | | ```c |
| | | scpi_command_t scpi_commands[] = { |
| | |
| | | }; |
| | | ``` |
| | | |
| | | Than you need to initialize interface callbacks structure. If you don't want to provide some callbacks, just initialize it as `NULL`. write callback is mandatory and is used to output data from the library. |
| | | Then you need to initialize the interface callbacks structure. If you don't want to provide some callbacks, just initialize it as `NULL`. The write callback is mandatory and is used to output data from the library. |
| | | |
| | | ```c |
| | | scpi_interface_t scpi_interface = { |
| | | .write = myWrite, |
| | | .error = NULL, |
| | | .reset = NULL, |
| | | .test = NULL, |
| | | .srq = NULL, |
| | | .reset = NULL, /* Called from SCPI_CoreRst */ |
| | | .test = NULL, /* Called from SCPI_CoreTstQ */ |
| | | .control = NULL, |
| | | }; |
| | | ``` |
| | | |
| | | Important thing is command buffer. Maximum size is up to you and it should be larger than any possible largest command. |
| | | An important library component is the command buffer. The maximum size is up to you and should be larger than the largest possible command. |
| | | |
| | | ```c |
| | | #define SCPI_INPUT_BUFFER_LENGTH 256 |
| | | static char scpi_input_buffer[SCPI_INPUT_BUFFER_LENGTH]; |
| | | ``` |
| | | |
| | | The last structure is scpi context used in parser library. |
| | | The last structure is the scpi context used in the parser library. |
| | | |
| | | ```c |
| | | scpi_t scpi_context = { |
| | |
| | | }; |
| | | ``` |
| | | |
| | | All these structures should be global variables of the c file or allocated by function like malloc. It is common mistake to create these structures inside a function as local variables of this function. This will not work. If you don't know why, you should read something about [function stack.](http://stackoverflow.com/questions/4824342/returning-a-local-variable-from-function-in-c). |
| | | All these structures should be global variables of the C file or allocated by function like malloc. It is a common mistake to create these structures inside a function as local variables of this function. This will not work. If you don't know why, you should read something about [function stack.](http://stackoverflow.com/questions/4824342/returning-a-local-variable-from-function-in-c). |
| | | |
| | | |
| | | Now we are ready to initialize SCPI context. It is possible to use more SCPI contexts and share some configurations (command list, registers, units list, error callback...) |
| | |
| | | SCPI_Init(&scpi_context); |
| | | ``` |
| | | |
| | | Test implementation of function myWrite, which outputs everything to stdout, can be |
| | | A test implementation of function myWrite, which outputs everything to stdout, could be |
| | | |
| | | ```c |
| | | size_t myWrite(scpi_context_t * context, const char * data, size_t len) { |
| | | size_t myWrite(scpi_t * context, const char * data, size_t len) { |
| | | (void) context; |
| | | return fwrite(data, 1, len, stdout); |
| | | } |
| | | ``` |
| | | |
| | | Interactive demo can beimplemented using this loop |
| | | An interactive demo can beimplemented using this loop |
| | | |
| | | ```c |
| | | #define SMALL_BUFFER_LEN |
| | |
| | | Implementation of command callback |
| | | ------------- |
| | | |
| | | Command callback is defined as function with result of type `scpi_result_t` and one parameter - scpi context |
| | | Command callback is defined as a function with return value of type `scpi_result_t` and one parameter - scpi context |
| | | |
| | | ```c |
| | | scpi_result_t DMM_MeasureVoltageDcQ(scpi_t * context) |
| | |
| | | |
| | | You can read command parameters and write command results. There are several functions to do this. |
| | | |
| | | Every time, you call function to read parameter, it shifts pointers to the next parameter. You can't read specified parameter directly by its index - e.g. |
| | | Every time you call a function to read a command parameter, it shifts pointers to the next parameter. You can't read specified parameter directly by its index - e.g. |
| | | |
| | | ```c |
| | | // pseudocode |
| | |
| | | |
| | | If you discard some parameters, there is no way to recover them. |
| | | |
| | | These are the functions, you can use to read parameters |
| | | These are the functions, you can use to read command parameters |
| | | - `SCPI_ParamInt` - read signed 32bit integer value (dec or hex with 0x prefix) |
| | | - `SCPI_ParamDouble` - read double value |
| | | - `SCPI_ParamNumber` - read double value with or without units or represented by special number (DEF, MIN, MAX, ...). This function is more universal then SCPI_ParamDouble. |
| | | - `SCPI_ParamText` - read text value - may be encapsuled in "" |
| | | - `SCPI_ParamString` - read unspecified parameter not encapsulated in "" |
| | | - `SCPI_ParamBool` - read boolean value (ON, OFF, 0, 1) |
| | | - `SCPI_ParamChoice` - read enumeration value eg. (BUS, IMMediate, EXTernal) defined by parameter |
| | | |
| | | These are the functions, you can use to write results |
| | | These are the functions, you can use to write command results |
| | | - `SCPI_ResultInt` - write integer value |
| | | - `SCPI_ResultDouble` - write double value |
| | | - `SCPI_ResultText` - write text value encapsulated in "" |
| | | - `SCPI_ResultString` - directly write string value |
| | | - `SCPI_ResultBool` - write boolean value |
| | | |
| | | You can use function `SCPI_NumberToStr` to convert number with units to textual representation and then use `SCPI_ResultString` to write this to the user. |
| | | You can use the function `SCPI_NumberToStr` to convert number with units to textual representation and then use `SCPI_ResultString` to write this to the user. |
| | |
| | | } |
| | | |
| | | |
| | | scpi_result_t DMM_MeasureVoltageAcQ(scpi_t * context) { |
| | | scpi_number_t param1, param2; |
| | | char bf[15]; |
| | | fprintf(stderr, "meas:volt:ac\r\n"); // debug command name |
| | | |
| | | // read first parameter if present |
| | | if (!SCPI_ParamNumber(context, ¶m1, false)) { |
| | | // do something, if parameter not present |
| | | } |
| | | |
| | | // read second paraeter if present |
| | | if (!SCPI_ParamNumber(context, ¶m2, false)) { |
| | | // do something, if parameter not present |
| | | } |
| | | |
| | | |
| | | SCPI_NumberToStr(context, ¶m1, bf, 15); |
| | | fprintf(stderr, "\tP1=%s\r\n", bf); |
| | | |
| | | |
| | | SCPI_NumberToStr(context, ¶m2, bf, 15); |
| | | fprintf(stderr, "\tP2=%s\r\n", bf); |
| | | |
| | | SCPI_ResultDouble(context, 0); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | scpi_result_t DMM_ConfigureVoltageDc(scpi_t * context) { |
| | | double param1, param2; |
| | | fprintf(stderr, "conf:volt:dc\r\n"); // debug command name |
| | |
| | | |
| | | fprintf(stderr, "\tP1=%lf\r\n", param1); |
| | | fprintf(stderr, "\tP2=%lf\r\n", param2); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | scpi_result_t TEST_Bool(scpi_t * context) { |
| | | scpi_bool_t param1; |
| | | fprintf(stderr, "TEST:BOOL\r\n"); // debug command name |
| | | |
| | | // read first parameter if present |
| | | if (!SCPI_ParamBool(context, ¶m1, true)) { |
| | | return SCPI_RES_ERR; |
| | | } |
| | | |
| | | fprintf(stderr, "\tP1=%d\r\n", param1); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | const char * trigger_source[] = { |
| | | "BUS", |
| | | "IMMediate", |
| | | "EXTernal", |
| | | NULL /* termination of option list */ |
| | | }; |
| | | |
| | | |
| | | scpi_result_t TEST_ChoiceQ(scpi_t * context) { |
| | | |
| | | int32_t param; |
| | | |
| | | if (!SCPI_ParamChoice(context, trigger_source, ¶m, true)) { |
| | | return SCPI_RES_ERR; |
| | | } |
| | | |
| | | fprintf(stderr, "\tP1=%s (%d)\r\n", trigger_source[param], param); |
| | | |
| | | SCPI_ResultInt(context, param); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | scpi_result_t TEST_Numbers(scpi_t * context) { |
| | | |
| | | fprintf(stderr, "RAW CMD %.*s\r\n", (int)context->paramlist.cmd_raw.length, context->paramlist.cmd_raw.data); |
| | | |
| | | return SCPI_RES_OK; |
| | | } |
| | |
| | | { .pattern = "*WAI", .callback = SCPI_CoreWai,}, |
| | | |
| | | /* Required SCPI commands (SCPI std V1999.0 4.2.1) */ |
| | | {.pattern = "SYSTem:ERRor?", .callback = SCPI_SystemErrorNextQ,}, |
| | | {.pattern = "SYSTem:ERRor:NEXT?", .callback = SCPI_SystemErrorNextQ,}, |
| | | {.pattern = "SYSTem:ERRor[:NEXT]?", .callback = SCPI_SystemErrorNextQ,}, |
| | | {.pattern = "SYSTem:ERRor:COUNt?", .callback = SCPI_SystemErrorCountQ,}, |
| | | {.pattern = "SYSTem:VERSion?", .callback = SCPI_SystemVersionQ,}, |
| | | |
| | |
| | | //{.pattern = "STATus:OPERation:ENABle", .callback = scpi_stub_callback,}, |
| | | //{.pattern = "STATus:OPERation:ENABle?", .callback = scpi_stub_callback,}, |
| | | |
| | | {.pattern = "STATus:QUEStionable?", .callback = SCPI_StatusQuestionableEventQ,}, |
| | | {.pattern = "STATus:QUEStionable:EVENt?", .callback = SCPI_StatusQuestionableEventQ,}, |
| | | {.pattern = "STATus:QUEStionable[:EVENt]?", .callback = SCPI_StatusQuestionableEventQ,}, |
| | | //{.pattern = "STATus:QUEStionable:CONDition?", .callback = scpi_stub_callback,}, |
| | | {.pattern = "STATus:QUEStionable:ENABle", .callback = SCPI_StatusQuestionableEnable,}, |
| | | {.pattern = "STATus:QUEStionable:ENABle?", .callback = SCPI_StatusQuestionableEnableQ,}, |
| | |
| | | {.pattern = "MEASure:VOLTage:DC?", .callback = DMM_MeasureVoltageDcQ,}, |
| | | {.pattern = "CONFigure:VOLTage:DC", .callback = DMM_ConfigureVoltageDc,}, |
| | | {.pattern = "MEASure:VOLTage:DC:RATio?", .callback = SCPI_StubQ,}, |
| | | {.pattern = "MEASure:VOLTage:AC?", .callback = SCPI_StubQ,}, |
| | | {.pattern = "MEASure:VOLTage:AC?", .callback = DMM_MeasureVoltageAcQ,}, |
| | | {.pattern = "MEASure:CURRent:DC?", .callback = SCPI_StubQ,}, |
| | | {.pattern = "MEASure:CURRent:AC?", .callback = SCPI_StubQ,}, |
| | | {.pattern = "MEASure:RESistance?", .callback = SCPI_StubQ,}, |
| | |
| | | {.pattern = "MEASure:PERiod?", .callback = SCPI_StubQ,}, |
| | | |
| | | {.pattern = "SYSTem:COMMunication:TCPIP:CONTROL?", .callback = SCPI_SystemCommTcpipControlQ,}, |
| | | |
| | | {.pattern = "TEST:BOOL", .callback = TEST_Bool,}, |
| | | {.pattern = "TEST:CHOice?", .callback = TEST_ChoiceQ,}, |
| | | {.pattern = "TEST#:NUMbers#", .callback = TEST_Numbers,}, |
| | | |
| | | SCPI_CMD_LIST_END |
| | | }; |
| | |
| | | .registers = scpi_regs, |
| | | .units = scpi_units_def, |
| | | .special_numbers = scpi_special_numbers_def, |
| | | .idn = {"MANUFACTURE", "INSTR2013", NULL, "01-02"}, |
| | | }; |
| | |
| | | /* units */ scpi_units_def, |
| | | /* special_numbers */ scpi_special_numbers_def, |
| | | /* user_context */ NULL, |
| | | /* idn */ {"MANUFACTURE", "INSTR2013", NULL, "01-02"}, |
| | | }; |
| | | |
| | |
| | | #define CONTROL_PORT 5026 |
| | | |
| | | #define SCPI_THREAD_PRIO (tskIDLE_PRIORITY + 2) |
| | | #define SCPI_THREAD_STACKSIZE (512) |
| | | |
| | | #define SCPI_MSG_TIMEOUT 0 |
| | | #define SCPI_MSG_TEST 1 |
| | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | /** |
| | | * Return 0 as OK and other number as error |
| | | */ |
| | | scpi_result_t SCPI_Test(scpi_t * context) { |
| | | (void) context; |
| | | iprintf("**Test\r\n"); |
| | | return SCPI_RES_OK; |
| | | return 0; |
| | | } |
| | | |
| | | scpi_result_t SCPI_Reset(scpi_t * context) { |
| | |
| | | } |
| | | |
| | | void scpi_server_init(void) { |
| | | sys_thread_new("SCPI", scpi_server_thread, NULL, 2 * DEFAULT_THREAD_STACKSIZE, SCPI_THREAD_PRIO); |
| | | sys_thread_new("SCPI", scpi_server_thread, NULL, SCPI_THREAD_STACKSIZE, SCPI_THREAD_PRIO); |
| | | } |
| | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | /** |
| | | * Return 0 as OK and other number as error |
| | | */ |
| | | scpi_result_t SCPI_Test(scpi_t * context) { |
| | | fprintf(stderr, "**Test\r\n"); |
| | | return SCPI_RES_OK; |
| | | return 0; |
| | | } |
| | | |
| | | scpi_result_t SCPI_Reset(scpi_t * context) { |
| | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | /** |
| | | * Return 0 as OK and other number as error |
| | | */ |
| | | scpi_result_t SCPI_Test(scpi_t * context) { |
| | | fprintf(stderr, "**Test\r\n"); |
| | | return SCPI_RES_OK; |
| | | return 0; |
| | | } |
| | | |
| | | scpi_result_t SCPI_Reset(scpi_t * context) { |
| | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | /** |
| | | * Return 0 as OK and other number as error |
| | | */ |
| | | scpi_result_t SCPI_Test(scpi_t * context) { |
| | | fprintf(stderr, "**Test\r\n"); |
| | | return SCPI_RES_OK; |
| | | return 0; |
| | | } |
| | | |
| | | scpi_result_t SCPI_Reset(scpi_t * context) { |
| | |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | /** |
| | | * Return 0 as OK and other number as error |
| | | */ |
| | | scpi_result_t SCPI_Test(scpi_t * context) { |
| | | fprintf(stderr, "**Test\r\n"); |
| | | return SCPI_RES_OK; |
| | | return 0; |
| | | } |
| | | |
| | | scpi_result_t SCPI_Reset(scpi_t * context) { |
| | |
| | | HDRS = $(addprefix inc/scpi/, \ |
| | | scpi.h constants.h debug.h error.h \ |
| | | fifo.h ieee488.h minimal.h parser.h \ |
| | | types.h units.h \ |
| | | ) src/utils.h |
| | | types.h units.h utils_private.h \ |
| | | ) |
| | | |
| | | |
| | | TESTS = $(addprefix test/, \ |
| | | test_fifo.c test_scpi_utils.c \ |
| | | test_fifo.c test_scpi_utils.c test_lib.c \ |
| | | ) |
| | | |
| | | TESTS_OBJS = $(TESTS:.c=.o) |
| | |
| | | #endif |
| | | |
| | | /* Compiler specific */ |
| | | /* 8bit PIC - PIC16, etc */ |
| | | #if defined(_MPC_) |
| | | #define HAVE_STRNLEN 0 |
| | | #define HAVE_STRNCASECMP 0 |
| | | #define HAVE_STRNICMP 1 |
| | | #endif |
| | | |
| | | /* PIC24 */ |
| | | #if defined(__C30__) |
| | | #define HAVE_STRNLEN 0 |
| | | #define HAVE_STRNCASECMP 0 |
| | | #define HAVE_STRNICMP 0 |
| | | #endif |
| | | |
| | | /* PIC32mx */ |
| | | #if defined(__C32__) |
| | | #define HAVE_STRNLEN 0 |
| | | #define HAVE_STRNCASECMP 1 |
| | | #define HAVE_STRNICMP 0 |
| | | #endif |
| | | |
| | | /* ======== test strnlen ======== */ |
| | |
| | | #ifndef HAVE_STRNCASECMP |
| | | #define HAVE_STRNCASECMP 1 |
| | | #endif |
| | | /* ======== test strnicmp ======== */ |
| | | #ifndef HAVE_STRNICMP |
| | | #define HAVE_STRNICMP 0 |
| | | #endif |
| | | |
| | | /* define local macros depending on existance of strnlen */ |
| | | #if HAVE_STRNLEN |
| | |
| | | #define SCPI_strnlen(s, l) BSD_strnlen((s), (l)) |
| | | #endif |
| | | |
| | | /* define local macros depending on existance of strncasecmp */ |
| | | /* define local macros depending on existance of strncasecmp and strnicmp */ |
| | | #if HAVE_STRNCASECMP |
| | | #define SCPI_strncasecmp(s1, s2, l) strncasecmp((s1), (s2), (l)) |
| | | #elif HAVE_STRNICMP |
| | | #define SCPI_strncasecmp(s1, s2, l) strnicmp((s1), (s2), (l)) |
| | | #else |
| | | #define SCPI_strncasecmp(s1, s2, l) strcasecmp((s1), (s2)) |
| | | #define SCPI_strncasecmp(s1, s2, l) OUR_strncasecmp((s1), (s2), (l)) |
| | | #endif |
| | | |
| | | #ifdef __cplusplus |
| | |
| | | #endif |
| | | |
| | | |
| | | #define SCPI_MANUFACTURE "CTU FEE" |
| | | #define SCPI_DEV_NAME "TEST SCPI INSTRUMENT TSI3225" |
| | | #define SCPI_DEV_VERSION "v1.0" |
| | | /* 4.1.3.6 *IDN? */ |
| | | |
| | | #define SCPI_DEFAULT_1_MANUFACTURE "CTU FEE" |
| | | #define SCPI_DEFAULT_2_MODEL "TSI3225" |
| | | #define SCPI_DEFAULT_3 "0" |
| | | #define SCPI_DEFAULT_4_REVISION "01-01" |
| | | |
| | | /* 21.21 :VERSion? |
| | | * YYYY.V |
| | | * YYYY = SCPI year |
| | | * V = SCPI revision |
| | | */ |
| | | #define SCPI_STD_VERSION_REVISION "1999.0" |
| | | |
| | | #ifdef __cplusplus |
| | | } |
| | |
| | | #define SCPI_DEBUG_COMMAND(a) |
| | | |
| | | |
| | | bool_t SCPI_DebugCommand(scpi_t * context); |
| | | scpi_bool_t SCPI_DebugCommand(scpi_t * context); |
| | | |
| | | |
| | | #ifdef __cplusplus |
| | |
| | | int32_t SCPI_ErrorCount(scpi_t * context); |
| | | const char * SCPI_ErrorTranslate(int16_t err); |
| | | |
| | | #define SCPI_ERROR_SYNTAX -102 |
| | | #define SCPI_ERROR_INVALID_SEPARATOR -103 |
| | | #define SCPI_ERROR_UNDEFINED_HEADER -113 |
| | | #define SCPI_ERROR_PARAMETER_NOT_ALLOWED -108 |
| | | #define SCPI_ERROR_MISSING_PARAMETER -109 |
| | | #define SCPI_ERROR_INVALID_SUFFIX -131 |
| | | #define SCPI_ERROR_SUFFIX_NOT_ALLOWED -138 |
| | | /* http://en.wikipedia.org/wiki/X_Macro */ |
| | | #define LIST_OF_ERRORS \ |
| | | X(SCPI_ERROR_SYNTAX, -102, "Syntax error") \ |
| | | X(SCPI_ERROR_INVALID_SEPARATOR, -103, "Invalid separator") \ |
| | | X(SCPI_ERROR_UNDEFINED_HEADER, -113, "Undefined header") \ |
| | | X(SCPI_ERROR_PARAMETER_NOT_ALLOWED,-108, "Parameter not allowed") \ |
| | | X(SCPI_ERROR_MISSING_PARAMETER, -109, "Missing parameter") \ |
| | | X(SCPI_ERROR_INVALID_SUFFIX, -131, "Invalid suffix") \ |
| | | X(SCPI_ERROR_SUFFIX_NOT_ALLOWED, -138, "Suffix not allowed") \ |
| | | X(SCPI_ERROR_EXECUTION_ERROR, -200, "Execution error") \ |
| | | X(SCPI_ERROR_ILLEGAL_PARAMETER_VALUE,-224,"Illegal parameter value") \ |
| | | |
| | | #define SCPI_ERROR_EXECUTION_ERROR -200 |
| | | #define SCPI_ERROR_ILLEGAL_PARAMETER_VALUE -224 |
| | | |
| | | enum { |
| | | #define X(def, val, str) def = val, |
| | | LIST_OF_ERRORS |
| | | #undef X |
| | | }; |
| | | |
| | | #ifdef __cplusplus |
| | | } |
| | |
| | | |
| | | void fifo_init(fifo_t * fifo); |
| | | void fifo_clear(fifo_t * fifo); |
| | | bool_t fifo_add(fifo_t * fifo, int16_t value); |
| | | bool_t fifo_remove(fifo_t * fifo, int16_t * value); |
| | | bool_t fifo_count(fifo_t * fifo, int16_t * value); |
| | | scpi_bool_t fifo_add(fifo_t * fifo, int16_t value); |
| | | scpi_bool_t fifo_remove(fifo_t * fifo, int16_t * value); |
| | | scpi_bool_t fifo_count(fifo_t * fifo, int16_t * value); |
| | | |
| | | #ifdef __cplusplus |
| | | } |
| | |
| | | void SCPI_RegSet(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t val); |
| | | void SCPI_RegSetBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits); |
| | | void SCPI_RegClearBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits); |
| | | void SCPI_EventClear(scpi_t * context); |
| | | |
| | | #ifdef __cplusplus |
| | | } |
| | |
| | | void SCPI_Init(scpi_t * context); |
| | | |
| | | int SCPI_Input(scpi_t * context, const char * data, size_t len); |
| | | int SCPI_Parse(scpi_t * context, const char * data, size_t len); |
| | | int SCPI_Parse(scpi_t * context, char * data, size_t len); |
| | | |
| | | scpi_bool_t SCPI_IsCmd(scpi_t * context, const char * cmd); |
| | | |
| | | size_t SCPI_ResultString(scpi_t * context, const char * data); |
| | | size_t SCPI_ResultInt(scpi_t * context, int32_t val); |
| | | size_t SCPI_ResultDouble(scpi_t * context, double val); |
| | | size_t SCPI_ResultText(scpi_t * context, const char * data); |
| | | size_t SCPI_ResultBool(scpi_t * context, bool_t val); |
| | | size_t SCPI_ResultBool(scpi_t * context, scpi_bool_t val); |
| | | |
| | | bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, bool_t mandatory); |
| | | bool_t SCPI_ParamDouble(scpi_t * context, double * value, bool_t mandatory); |
| | | bool_t SCPI_ParamString(scpi_t * context, const char ** value, size_t * len, bool_t mandatory); |
| | | bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, bool_t mandatory); |
| | | bool_t SCPI_ParamBool(scpi_t * context, bool_t * value, bool_t mandatory); |
| | | bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], size_t * value, bool_t mandatory); |
| | | scpi_bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, scpi_bool_t mandatory); |
| | | scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory); |
| | | scpi_bool_t SCPI_ParamString(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory); |
| | | scpi_bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory); |
| | | scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory); |
| | | scpi_bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], int32_t * value, scpi_bool_t mandatory); |
| | | |
| | | |
| | | #ifdef __cplusplus |
| | |
| | | #endif |
| | | |
| | | /* basic data types */ |
| | | typedef bool bool_t; |
| | | /* typedef enum { FALSE = 0, TRUE } bool_t; */ |
| | | typedef bool scpi_bool_t; |
| | | /* typedef enum { FALSE = 0, TRUE } scpi_bool_t; */ |
| | | |
| | | /* IEEE 488.2 registers */ |
| | | enum _scpi_reg_name_t { |
| | |
| | | |
| | | typedef struct _scpi_command_t scpi_command_t; |
| | | |
| | | struct _scpi_buffer_t { |
| | | size_t length; |
| | | size_t position; |
| | | char * data; |
| | | }; |
| | | typedef struct _scpi_buffer_t scpi_buffer_t; |
| | | |
| | | struct _scpi_const_buffer_t { |
| | | size_t length; |
| | | size_t position; |
| | | const char * data; |
| | | }; |
| | | typedef struct _scpi_const_buffer_t scpi_const_buffer_t; |
| | | |
| | | struct _scpi_param_list_t { |
| | | const scpi_command_t * cmd; |
| | | const char * parameters; |
| | | size_t length; |
| | | scpi_const_buffer_t cmd_raw; |
| | | }; |
| | | #define SCPI_CMD_LIST_END {NULL, NULL, } |
| | | typedef struct _scpi_param_list_t scpi_param_list_t; |
| | |
| | | /* scpi interface */ |
| | | typedef struct _scpi_t scpi_t; |
| | | typedef struct _scpi_interface_t scpi_interface_t; |
| | | |
| | | struct _scpi_buffer_t { |
| | | size_t length; |
| | | size_t position; |
| | | char * data; |
| | | }; |
| | | typedef struct _scpi_buffer_t scpi_buffer_t; |
| | | |
| | | typedef size_t(*scpi_write_t)(scpi_t * context, const char * data, size_t len); |
| | | typedef scpi_result_t(*scpi_write_control_t)(scpi_t * context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val); |
| | |
| | | SCPI_NUM_DOWN, |
| | | SCPI_NUM_NAN, |
| | | SCPI_NUM_INF, |
| | | SCPI_NUM_NINF |
| | | SCPI_NUM_NINF, |
| | | SCPI_NUM_AUTO |
| | | }; |
| | | typedef enum _scpi_special_number_t scpi_special_number_t; |
| | | |
| | |
| | | scpi_interface_t * interface; |
| | | int_fast16_t output_count; |
| | | int_fast16_t input_count; |
| | | bool_t cmd_error; |
| | | scpi_bool_t cmd_error; |
| | | scpi_error_queue_t error_queue; |
| | | scpi_reg_val_t * registers; |
| | | const scpi_unit_def_t * units; |
| | | const scpi_special_number_def_t * special_numbers; |
| | | void * user_context; |
| | | const char * idn[4]; |
| | | }; |
| | | |
| | | #ifdef __cplusplus |
| | |
| | | extern const scpi_unit_def_t scpi_units_def[]; |
| | | extern const scpi_special_number_def_t scpi_special_numbers_def[]; |
| | | |
| | | bool_t SCPI_ParamNumber(scpi_t * context, scpi_number_t * value, bool_t mandatory); |
| | | scpi_bool_t SCPI_ParamNumber(scpi_t * context, scpi_number_t * value, scpi_bool_t mandatory); |
| | | size_t SCPI_NumberToStr(scpi_t * context, scpi_number_t * value, char * str, size_t len); |
| | | |
| | | #ifdef __cplusplus |
File was renamed from libscpi/src/utils.h |
| | |
| | | #define LOCAL |
| | | #endif |
| | | |
| | | char * strnpbrk(const char *str, size_t size, const char *set) LOCAL; |
| | | bool_t compareStr(const char * str1, size_t len1, const char * str2, size_t len2) LOCAL; |
| | | const char * strnpbrk(const char *str, size_t size, const char *set) LOCAL; |
| | | scpi_bool_t compareStr(const char * str1, size_t len1, const char * str2, size_t len2) LOCAL; |
| | | scpi_bool_t compareStrAndNum(const char * str1, size_t len1, const char * str2, size_t len2) LOCAL; |
| | | size_t longToStr(int32_t val, char * str, size_t len) LOCAL; |
| | | size_t doubleToStr(double val, char * str, size_t len) LOCAL; |
| | | size_t strToLong(const char * str, int32_t * val) LOCAL; |
| | | size_t strToDouble(const char * str, double * val) LOCAL; |
| | | bool_t locateText(const char * str1, size_t len1, const char ** str2, size_t * len2) LOCAL; |
| | | bool_t locateStr(const char * str1, size_t len1, const char ** str2, size_t * len2) LOCAL; |
| | | scpi_bool_t locateText(const char * str1, size_t len1, const char ** str2, size_t * len2) LOCAL; |
| | | scpi_bool_t locateStr(const char * str1, size_t len1, const char ** str2, size_t * len2) LOCAL; |
| | | size_t skipWhitespace(const char * cmd, size_t len) LOCAL; |
| | | size_t skipColon(const char * cmd, size_t len) LOCAL; |
| | | bool_t matchPattern(const char * pattern, size_t pattern_len, const char * str, size_t str_len) LOCAL; |
| | | bool_t matchCommand(const char * pattern, const char * cmd, size_t len) LOCAL; |
| | | scpi_bool_t matchPattern(const char * pattern, size_t pattern_len, const char * str, size_t str_len) LOCAL; |
| | | scpi_bool_t matchCommand(const char * pattern, const char * cmd, size_t len) LOCAL; |
| | | scpi_bool_t composeCompoundCommand(char * ptr_prev, size_t len_prev, char ** pptr, size_t * plen); |
| | | |
| | | #if !HAVE_STRNLEN |
| | | size_t BSD_strnlen(const char *s, size_t maxlen); |
| | | #endif |
| | | |
| | | #if !HAVE_STRNCASECMP && !HAVE_STRNICMP |
| | | int OUR_strncasecmp(const char *s1, const char *s2, size_t n); |
| | | #endif |
| | | |
| | | #define min(a, b) (((a) < (b)) ? (a) : (b)) |
| | | #define max(a, b) (((a) > (b)) ? (a) : (b)) |
| | | |
| | |
| | | * @param context |
| | | * @return |
| | | */ |
| | | bool_t SCPI_DebugCommand(scpi_t * context) { |
| | | scpi_bool_t SCPI_DebugCommand(scpi_t * context) { |
| | | size_t res; |
| | | printf("**DEBUG: %s (\"", context->paramlist.cmd->pattern); |
| | | res = fwrite(context->paramlist.parameters, 1, context->paramlist.length, stdout); |
| | |
| | | const char * SCPI_ErrorTranslate(int16_t err) { |
| | | switch (err) { |
| | | case 0: return "No error"; |
| | | case SCPI_ERROR_SYNTAX: return "Syntax error"; |
| | | case SCPI_ERROR_INVALID_SEPARATOR: return "Invalid separator"; |
| | | case SCPI_ERROR_UNDEFINED_HEADER: return "Undefined header"; |
| | | case SCPI_ERROR_PARAMETER_NOT_ALLOWED: return "Parameter not allowed"; |
| | | case SCPI_ERROR_MISSING_PARAMETER: return "Missing parameter"; |
| | | case SCPI_ERROR_INVALID_SUFFIX: return "Invalid suffix"; |
| | | case SCPI_ERROR_SUFFIX_NOT_ALLOWED: return "Suffix not allowed"; |
| | | case SCPI_ERROR_ILLEGAL_PARAMETER_VALUE: return "Illegal parameter value"; |
| | | #define X(def, val, str) case def: return str; |
| | | LIST_OF_ERRORS |
| | | #undef X |
| | | default: return "Unknown error"; |
| | | } |
| | | } |
| | |
| | | fifo->rd = 0; |
| | | } |
| | | |
| | | bool_t fifo_add(fifo_t * fifo, int16_t value) { |
| | | scpi_bool_t fifo_add(fifo_t * fifo, int16_t value) { |
| | | /* FIFO full? */ |
| | | if (fifo->wr == ((fifo->rd + fifo->size) % (fifo->size + 1))) { |
| | | fifo_remove(fifo, NULL); |
| | |
| | | return TRUE; |
| | | } |
| | | |
| | | bool_t fifo_remove(fifo_t * fifo, int16_t * value) { |
| | | scpi_bool_t fifo_remove(fifo_t * fifo, int16_t * value) { |
| | | /* FIFO empty? */ |
| | | if (fifo->wr == fifo->rd) { |
| | | return FALSE; |
| | |
| | | return TRUE; |
| | | } |
| | | |
| | | bool_t fifo_count(fifo_t * fifo, int16_t * value) { |
| | | scpi_bool_t fifo_count(fifo_t * fifo, int16_t * value) { |
| | | *value = fifo->wr - fifo->rd; |
| | | if (*value < 0) { |
| | | *value += (fifo->size + 1); |
| | |
| | | * @param val - new value |
| | | */ |
| | | void SCPI_RegSet(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t val) { |
| | | bool_t srq = FALSE; |
| | | scpi_bool_t srq = FALSE; |
| | | scpi_reg_val_t mask; |
| | | scpi_reg_val_t old_val; |
| | | |
| | |
| | | |
| | | /** |
| | | * *IDN? |
| | | * |
| | | * field1: MANUFACTURE |
| | | * field2: MODEL |
| | | * field4: SUBSYSTEMS REVISIONS |
| | | * |
| | | * example: MANUFACTURE,MODEL,0,01-02-01 |
| | | * @param context |
| | | * @return |
| | | */ |
| | | scpi_result_t SCPI_CoreIdnQ(scpi_t * context) { |
| | | SCPI_ResultString(context, SCPI_MANUFACTURE); |
| | | SCPI_ResultString(context, SCPI_DEV_NAME); |
| | | SCPI_ResultString(context, SCPI_DEV_VERSION); |
| | | SCPI_ResultString(context, context->idn[0]); |
| | | SCPI_ResultString(context, context->idn[1]); |
| | | SCPI_ResultString(context, context->idn[2]); |
| | | SCPI_ResultString(context, context->idn[3]); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | |
| | | * @return |
| | | */ |
| | | scpi_result_t SCPI_SystemVersionQ(scpi_t * context) { |
| | | SCPI_ResultString(context, SCPI_DEV_VERSION); |
| | | SCPI_ResultString(context, SCPI_STD_VERSION_REVISION); |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | |
| | | |
| | | #include "scpi/config.h" |
| | | #include "scpi/parser.h" |
| | | #include "utils.h" |
| | | #include "scpi/utils_private.h" |
| | | #include "scpi/error.h" |
| | | #include "scpi/constants.h" |
| | | |
| | | |
| | | static size_t cmdTerminatorPos(const char * cmd, size_t len); |
| | | static size_t cmdlineSeparatorPos(const char * cmd, size_t len); |
| | | static const char * cmdlineSeparator(const char * cmd, size_t len); |
| | | static const char * cmdlineTerminator(const char * cmd, size_t len); |
| | | static const char * cmdlineNext(const char * cmd, size_t len); |
| | | static size_t skipCmdLine(const char * cmd, size_t len); |
| | | |
| | | static void paramSkipBytes(scpi_t * context, size_t num); |
| | | static void paramSkipWhitespace(scpi_t * context); |
| | | static bool_t paramNext(scpi_t * context, bool_t mandatory); |
| | | static scpi_bool_t paramNext(scpi_t * context, scpi_bool_t mandatory); |
| | | |
| | | /* |
| | | int _strnicmp(const char* s1, const char* s2, size_t len) { |
| | |
| | | * Find next part of command |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return Pointer to next part of command |
| | | * @return number of characters to be skipped |
| | | */ |
| | | const char * cmdlineNext(const char * cmd, size_t len) { |
| | | size_t skipCmdLine(const char * cmd, size_t len) { |
| | | const char * separator = cmdlineSeparator(cmd, len); |
| | | if (separator == NULL) { |
| | | return cmd + len; |
| | | return len; |
| | | } else { |
| | | return separator + 1; |
| | | return separator + 1 - cmd; |
| | | } |
| | | } |
| | | |
| | |
| | | * @param context |
| | | * @result TRUE if context->paramlist is filled with correct values |
| | | */ |
| | | static bool_t findCommand(scpi_t * context, const char * cmdline_ptr, size_t cmdline_len, size_t cmd_len) { |
| | | static scpi_bool_t findCommand(scpi_t * context, const char * cmdline_ptr, size_t cmdline_len, size_t cmd_len) { |
| | | int32_t i; |
| | | const scpi_command_t * cmd; |
| | | |
| | |
| | | context->paramlist.cmd = cmd; |
| | | context->paramlist.parameters = cmdline_ptr + cmd_len; |
| | | context->paramlist.length = cmdline_len - cmd_len; |
| | | context->paramlist.cmd_raw.data = cmdline_ptr; |
| | | context->paramlist.cmd_raw.length = cmd_len; |
| | | context->paramlist.cmd_raw.position = 0; |
| | | return TRUE; |
| | | } |
| | | } |
| | |
| | | * @param len - command line length |
| | | * @return 1 if the last evaluated command was found |
| | | */ |
| | | int SCPI_Parse(scpi_t * context, const char * data, size_t len) { |
| | | int SCPI_Parse(scpi_t * context, char * data, size_t len) { |
| | | int result = 0; |
| | | const char * cmdline_end = data + len; |
| | | const char * cmdline_ptr = data; |
| | | char * cmdline_ptr = data; |
| | | size_t cmd_len; |
| | | size_t cmdline_len; |
| | | char * cmdline_ptr_prev = NULL; |
| | | size_t cmd_len_prev = 0; |
| | | |
| | | if (context == NULL) { |
| | | return -1; |
| | |
| | | while (cmdline_ptr < cmdline_end) { |
| | | result = 0; |
| | | cmd_len = cmdTerminatorPos(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | cmdline_len = cmdlineSeparatorPos(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | if (cmd_len > 0) { |
| | | composeCompoundCommand(cmdline_ptr_prev, cmd_len_prev, |
| | | &cmdline_ptr, &cmd_len); |
| | | cmdline_len = cmdlineSeparatorPos(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | if(findCommand(context, cmdline_ptr, cmdline_len, cmd_len)) { |
| | | processCommand(context); |
| | | result = 1; |
| | | cmdline_ptr_prev = cmdline_ptr; |
| | | cmd_len_prev = cmd_len; |
| | | } else { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_UNDEFINED_HEADER); |
| | | } |
| | | } |
| | | cmdline_ptr = cmdlineNext(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | cmdline_ptr += skipCmdLine(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | cmdline_ptr += skipWhitespace(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | } |
| | | return result; |
| | |
| | | * @param interface |
| | | */ |
| | | void SCPI_Init(scpi_t * context) { |
| | | if (context->idn[0] == NULL) { |
| | | context->idn[0] = SCPI_DEFAULT_1_MANUFACTURE; |
| | | } |
| | | if (context->idn[1] == NULL) { |
| | | context->idn[1] = SCPI_DEFAULT_2_MODEL; |
| | | } |
| | | if (context->idn[2] == NULL) { |
| | | context->idn[2] = SCPI_DEFAULT_3; |
| | | } |
| | | if (context->idn[3] == NULL) { |
| | | context->idn[3] = SCPI_DEFAULT_4_REVISION; |
| | | } |
| | | |
| | | context->buffer.position = 0; |
| | | SCPI_ErrorInit(context); |
| | | } |
| | |
| | | |
| | | ws = skipWhitespace(context->buffer.data, context->buffer.position); |
| | | cmd_term = cmdlineTerminator(context->buffer.data + ws, context->buffer.position - ws); |
| | | if (cmd_term != NULL) { |
| | | while (cmd_term != NULL) { |
| | | int curr_len = cmd_term - context->buffer.data; |
| | | result = SCPI_Parse(context, context->buffer.data + ws, curr_len - ws); |
| | | memmove(context->buffer.data, cmd_term, context->buffer.position - curr_len); |
| | | context->buffer.position -= curr_len; |
| | | |
| | | ws = skipWhitespace(context->buffer.data, context->buffer.position); |
| | | cmd_term = cmdlineTerminator(context->buffer.data + ws, context->buffer.position - ws); |
| | | } |
| | | } |
| | | |
| | |
| | | * @param val |
| | | * @return |
| | | */ |
| | | size_t SCPI_ResultBool(scpi_t * context, bool_t val) { |
| | | size_t SCPI_ResultBool(scpi_t * context, scpi_bool_t val) { |
| | | return SCPI_ResultInt(context, val ? 1 : 0); |
| | | } |
| | | |
| | |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t paramNext(scpi_t * context, bool_t mandatory) { |
| | | scpi_bool_t paramNext(scpi_t * context, scpi_bool_t mandatory) { |
| | | paramSkipWhitespace(context); |
| | | if (context->paramlist.length == 0) { |
| | | if (mandatory) { |
| | |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, bool_t mandatory) { |
| | | scpi_bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, scpi_bool_t mandatory) { |
| | | const char * param; |
| | | size_t param_len; |
| | | size_t num_len; |
| | |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamDouble(scpi_t * context, double * value, bool_t mandatory) { |
| | | scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory) { |
| | | const char * param; |
| | | size_t param_len; |
| | | size_t num_len; |
| | |
| | | /** |
| | | * Parse string parameter |
| | | * @param context |
| | | * @param value |
| | | * @param len |
| | | * @param value Pointer to string buffer where pointer to non-null terminated string will be returned |
| | | * @param len Length of returned non-null terminated string |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamString(scpi_t * context, const char ** value, size_t * len, bool_t mandatory) { |
| | | scpi_bool_t SCPI_ParamString(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) { |
| | | size_t length; |
| | | |
| | | if (!value || !len) { |
| | |
| | | /** |
| | | * Parse text parameter (can be inside "") |
| | | * @param context |
| | | * @param value |
| | | * @param len |
| | | * @param value Pointer to string buffer where pointer to non-null terminated string will be returned |
| | | * @param len Length of returned non-null terminated string |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, bool_t mandatory) { |
| | | scpi_bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) { |
| | | size_t length; |
| | | |
| | | if (!value || !len) { |
| | |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamBool(scpi_t * context, bool_t * value, bool_t mandatory) { |
| | | scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory) { |
| | | const char * param; |
| | | size_t param_len; |
| | | size_t num_len; |
| | |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], size_t * value, bool_t mandatory) { |
| | | scpi_bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], int32_t * value, scpi_bool_t mandatory) { |
| | | const char * param; |
| | | size_t param_len; |
| | | size_t res; |
| | |
| | | return FALSE; |
| | | } |
| | | |
| | | scpi_bool_t SCPI_IsCmd(scpi_t * context, const char * cmd) { |
| | | if (! context->paramlist.cmd) { |
| | | return FALSE; |
| | | } |
| | | |
| | | const char * pattern = context->paramlist.cmd->pattern; |
| | | return matchCommand (pattern, cmd, strlen (cmd)); |
| | | } |
| | |
| | | #include <string.h> |
| | | #include "scpi/parser.h" |
| | | #include "scpi/units.h" |
| | | #include "utils.h" |
| | | #include "scpi/utils_private.h" |
| | | #include "scpi/error.h" |
| | | |
| | | |
| | |
| | | {/* name */ "UP", /* type */ SCPI_NUM_UP}, |
| | | {/* name */ "DOWN", /* type */ SCPI_NUM_DOWN}, |
| | | {/* name */ "NAN", /* type */ SCPI_NUM_NAN}, |
| | | {/* name */ "INF", /* type */ SCPI_NUM_INF}, |
| | | {/* name */ "INFinity", /* type */ SCPI_NUM_INF}, |
| | | {/* name */ "NINF", /* type */ SCPI_NUM_NINF}, |
| | | {/* name */ "AUTO", /* type */ SCPI_NUM_AUTO}, |
| | | SCPI_SPECIAL_NUMBERS_LIST_END, |
| | | }; |
| | | |
| | |
| | | * @param value resultin value |
| | | * @return TRUE if str matches one of specs patterns |
| | | */ |
| | | static bool_t translateSpecialNumber(const scpi_special_number_def_t * specs, const char * str, size_t len, scpi_number_t * value) { |
| | | static scpi_bool_t translateSpecialNumber(const scpi_special_number_def_t * specs, const char * str, size_t len, scpi_number_t * value) { |
| | | int i; |
| | | |
| | | value->value = 0.0; |
| | |
| | | * @param value preparsed numeric value |
| | | * @return TRUE if value parameter was converted to base units |
| | | */ |
| | | static bool_t transformNumber(scpi_t * context, const char * unit, size_t len, scpi_number_t * value) { |
| | | static scpi_bool_t transformNumber(scpi_t * context, const char * unit, size_t len, scpi_number_t * value) { |
| | | size_t s; |
| | | const scpi_unit_def_t * unitDef; |
| | | s = skipWhitespace(unit, len); |
| | |
| | | * @param mandatory if the parameter is mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamNumber(scpi_t * context, scpi_number_t * value, bool_t mandatory) { |
| | | bool_t result; |
| | | scpi_bool_t SCPI_ParamNumber(scpi_t * context, scpi_number_t * value, scpi_bool_t mandatory) { |
| | | scpi_bool_t result; |
| | | const char * param; |
| | | size_t len; |
| | | size_t numlen; |
| | |
| | | #include <string.h> |
| | | #include <ctype.h> |
| | | |
| | | #include "utils.h" |
| | | #include "scpi/utils_private.h" |
| | | |
| | | static size_t patternSeparatorShortPos(const char * pattern, size_t len); |
| | | static size_t patternSeparatorPos(const char * pattern, size_t len); |
| | |
| | | * @param set |
| | | * @return |
| | | */ |
| | | char * strnpbrk(const char *str, size_t size, const char *set) { |
| | | const char * strnpbrk(const char *str, size_t size, const char *set) { |
| | | const char *scanp; |
| | | long c, sc; |
| | | const char * strend = str + size; |
| | |
| | | while ((strend != str) && ((c = *str++) != 0)) { |
| | | for (scanp = set; (sc = *scanp++) != '\0';) |
| | | if (sc == c) |
| | | return ((char *) (str - 1)); |
| | | return str - 1; |
| | | } |
| | | return (NULL); |
| | | } |
| | |
| | | * @param len2 |
| | | * @return TRUE if len1==len2 and "len" characters of both strings are equal |
| | | */ |
| | | bool_t compareStr(const char * str1, size_t len1, const char * str2, size_t len2) { |
| | | scpi_bool_t compareStr(const char * str1, size_t len1, const char * str2, size_t len2) { |
| | | if (len1 != len2) { |
| | | return FALSE; |
| | | } |
| | |
| | | } |
| | | |
| | | return FALSE; |
| | | } |
| | | |
| | | /** |
| | | * Compare two strings, one be longer but may contains only numbers in that section |
| | | * @param str1 |
| | | * @param len1 |
| | | * @param str2 |
| | | * @param len2 |
| | | * @return TRUE if strings match |
| | | */ |
| | | scpi_bool_t compareStrAndNum(const char * str1, size_t len1, const char * str2, size_t len2) { |
| | | scpi_bool_t result = FALSE; |
| | | size_t i; |
| | | |
| | | if (len2 < len1) { |
| | | return FALSE; |
| | | } |
| | | |
| | | if (SCPI_strncasecmp(str1, str2, len1) == 0) { |
| | | result = TRUE; |
| | | } |
| | | |
| | | for (i = len1; i<len2; i++) { |
| | | if (!isdigit((int) str2[i])) { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | enum _locate_text_states { |
| | |
| | | /** |
| | | * Test locate text state, if it is correct final state |
| | | */ |
| | | static bool_t isFinalState(locate_text_states state) { |
| | | static scpi_bool_t isFinalState(locate_text_states state) { |
| | | return ( |
| | | ((state) == STATE_COMMA) |
| | | || ((state) == STATE_LAST_WHITESPACE) |
| | |
| | | * @param nfa stores automaton state |
| | | * @param c current char processed |
| | | */ |
| | | static bool_t locateTextAutomaton(locate_text_nfa * nfa, unsigned char c) { |
| | | static scpi_bool_t locateTextAutomaton(locate_text_nfa * nfa, unsigned char c) { |
| | | switch(nfa->state) { |
| | | /* first state locating only white spaces */ |
| | | case STATE_FIRST_WHITESPACE: |
| | |
| | | * @param len2 length of result |
| | | * @return string str1 contains text and str2 was set |
| | | */ |
| | | bool_t locateText(const char * str1, size_t len1, const char ** str2, size_t * len2) { |
| | | scpi_bool_t locateText(const char * str1, size_t len1, const char ** str2, size_t * len2) { |
| | | locate_text_nfa nfa; |
| | | nfa.state = STATE_FIRST_WHITESPACE; |
| | | nfa.startIdx = 0; |
| | |
| | | * @param nfa stores automaton state |
| | | * @param c current char processed |
| | | */ |
| | | static bool_t locateStrAutomaton(locate_text_nfa * nfa, unsigned char c) { |
| | | static scpi_bool_t locateStrAutomaton(locate_text_nfa * nfa, unsigned char c) { |
| | | switch(nfa->state) { |
| | | /* first state locating only white spaces */ |
| | | case STATE_FIRST_WHITESPACE: |
| | |
| | | * @param len2 length of result |
| | | * @return string str1 contains text and str2 was set |
| | | */ |
| | | bool_t locateStr(const char * str1, size_t len1, const char ** str2, size_t * len2) { |
| | | scpi_bool_t locateStr(const char * str1, size_t len1, const char ** str2, size_t * len2) { |
| | | locate_text_nfa nfa; |
| | | nfa.state = STATE_FIRST_WHITESPACE; |
| | | nfa.startIdx = 0; |
| | |
| | | } |
| | | |
| | | /** |
| | | * is colon or not |
| | | * @param cmd - command |
| | | * @return |
| | | */ |
| | | static bool_t iscolon(char ch) { |
| | | return (':' == ch) ? TRUE : FALSE; |
| | | } |
| | | |
| | | /** |
| | | * Pattern is composed from upper case an lower case letters. This function |
| | | * search the first lowercase letter |
| | | * @param pattern |
| | |
| | | */ |
| | | size_t patternSeparatorPos(const char * pattern, size_t len) { |
| | | |
| | | char * separator = strnpbrk(pattern, len, "?:[]"); |
| | | const char * separator = strnpbrk(pattern, len, "?:[]"); |
| | | if (separator == NULL) { |
| | | return len; |
| | | } else { |
| | |
| | | * @return position of separator or len |
| | | */ |
| | | size_t cmdSeparatorPos(const char * cmd, size_t len) { |
| | | char * separator = strnpbrk(cmd, len, ":?"); |
| | | const char * separator = strnpbrk(cmd, len, ":?"); |
| | | size_t result; |
| | | if (separator == NULL) { |
| | | result = len; |
| | |
| | | return result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Match pattern and str. Pattern is in format UPPERCASElowercase |
| | | * @param pattern |
| | |
| | | * @param str_len |
| | | * @return |
| | | */ |
| | | bool_t matchPattern(const char * pattern, size_t pattern_len, const char * str, size_t str_len) { |
| | | int pattern_sep_pos_short = patternSeparatorShortPos(pattern, pattern_len); |
| | | scpi_bool_t matchPattern(const char * pattern, size_t pattern_len, const char * str, size_t str_len) { |
| | | int pattern_sep_pos_short; |
| | | |
| | | if (pattern[pattern_len - 1] == '#') { |
| | | size_t new_pattern_len = pattern_len - 1; |
| | | |
| | | pattern_sep_pos_short = patternSeparatorShortPos(pattern, new_pattern_len); |
| | | |
| | | return compareStrAndNum(pattern, new_pattern_len, str, str_len) || |
| | | compareStrAndNum(pattern, pattern_sep_pos_short, str, str_len); |
| | | } else { |
| | | |
| | | pattern_sep_pos_short = patternSeparatorShortPos(pattern, pattern_len); |
| | | |
| | | return compareStr(pattern, pattern_len, str, str_len) || |
| | | compareStr(pattern, pattern_sep_pos_short, str, str_len); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Compare pattern and command |
| | | * @param pattern |
| | | * @param pattern eg. [:MEASure]:VOLTage:DC? |
| | | * @param cmd - command |
| | | * @param len - max search length |
| | | * @return TRUE if pattern matches, FALSE otherwise |
| | | */ |
| | | bool_t matchCommand(const char * pattern, const char * cmd, size_t len) { |
| | | int result = FALSE; |
| | | scpi_bool_t matchCommand(const char * pattern, const char * cmd, size_t len) { |
| | | scpi_bool_t result = FALSE; |
| | | int leftFlag = 0; // flag for '[' on left |
| | | int rightFlag = 0; // flag for ']' on right |
| | | int cmd_sep_pos = 0; |
| | | |
| | | const char * pattern_ptr = pattern; |
| | | int pattern_len = strlen(pattern); |
| | |
| | | size_t cmd_len = SCPI_strnlen(cmd, len); |
| | | const char * cmd_end = cmd + cmd_len; |
| | | |
| | | /* TODO: now it is possible to send command ":*IDN?" which is incorrect */ |
| | | if (iscolon(cmd_ptr[0])) { |
| | | /* now support optional keywords in pattern style, e.g. [:MEASure]:VOLTage:DC? */ |
| | | if (pattern_ptr[0] == '[') { // skip first '[' |
| | | pattern_len--; |
| | | pattern_ptr++; |
| | | leftFlag++; |
| | | } |
| | | if (pattern_ptr[0] == ':') { // skip first ':' |
| | | pattern_len--; |
| | | pattern_ptr++; |
| | | } |
| | | |
| | | if (cmd_ptr[0] == ':') { |
| | | /* handle errornouse ":*IDN?" */ |
| | | if((cmd_len >= 2) && (cmd_ptr[1] != '*')) { |
| | | cmd_len --; |
| | | cmd_ptr ++; |
| | | } |
| | | } |
| | | |
| | | while (1) { |
| | | int pattern_sep_pos = patternSeparatorPos(pattern_ptr, pattern_end - pattern_ptr); |
| | | int cmd_sep_pos = cmdSeparatorPos(cmd_ptr, cmd_end - cmd_ptr); |
| | | |
| | | if ((leftFlag > 0) && (rightFlag > 0)) { |
| | | leftFlag--; |
| | | rightFlag--; |
| | | } else { |
| | | cmd_sep_pos = cmdSeparatorPos(cmd_ptr, cmd_end - cmd_ptr); |
| | | } |
| | | |
| | | if (matchPattern(pattern_ptr, pattern_sep_pos, cmd_ptr, cmd_sep_pos)) { |
| | | pattern_ptr = pattern_ptr + pattern_sep_pos; |
| | |
| | | |
| | | /* command complete, but pattern not */ |
| | | if (cmd_ptr >= cmd_end) { |
| | | if (cmd_end == cmd_ptr) { |
| | | if (cmd_ptr[0] == pattern_ptr[pattern_end - pattern_ptr - 1]) { |
| | | break; /* exist optional keyword, command is complete */ |
| | | } |
| | | if (']' == pattern_ptr[pattern_end - pattern_ptr - 1]) { |
| | | break; /* exist optional keyword, command is complete */ |
| | | } |
| | | } |
| | | result = FALSE; |
| | | break; |
| | | } |
| | |
| | | if ((pattern_ptr[0] == cmd_ptr[0]) && ((pattern_ptr[0] == ':') || (pattern_ptr[0] == '?'))) { |
| | | pattern_ptr = pattern_ptr + 1; |
| | | cmd_ptr = cmd_ptr + 1; |
| | | } else if ((pattern_ptr[1] == cmd_ptr[0]) |
| | | && (pattern_ptr[0] == '[') |
| | | && (pattern_ptr[1] == ':')) { |
| | | pattern_ptr = pattern_ptr + 2; // for skip '[' in "[:" |
| | | cmd_ptr = cmd_ptr + 1; |
| | | leftFlag++; |
| | | } else if ((pattern_ptr[1] == cmd_ptr[0]) |
| | | && (pattern_ptr[0] == ']') |
| | | && (pattern_ptr[1] == ':')) { |
| | | pattern_ptr = pattern_ptr + 2; // for skip ']' in "]:" |
| | | cmd_ptr = cmd_ptr + 1; |
| | | } else if ((pattern_ptr[2] == cmd_ptr[0]) |
| | | && (pattern_ptr[0] == ']') |
| | | && (pattern_ptr[1] == '[') |
| | | && (pattern_ptr[2] == ':')) { |
| | | pattern_ptr = pattern_ptr + 3; // for skip '][' in "][:" |
| | | cmd_ptr = cmd_ptr + 1; |
| | | leftFlag++; |
| | | } else if (((pattern_ptr[0] == ']') |
| | | || (pattern_ptr[0] == '[')) |
| | | && (*(pattern_end - 1) == '?') // last is '?' |
| | | && (cmd_ptr[0] == '?')) { |
| | | result = TRUE; // exist optional keyword, and they are end with '?' |
| | | break; // command is complete OK |
| | | } else { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | } else { |
| | | pattern_ptr = pattern_ptr + pattern_sep_pos; |
| | | if ((pattern_ptr[0] == ']') && (pattern_ptr[1] == ':')) { |
| | | pattern_ptr = pattern_ptr + 2; // for skip ']' in "]:" , pattern_ptr continue, while cmd_ptr remain unchanged |
| | | rightFlag++; |
| | | } else if ((pattern_ptr[0] == ']') |
| | | && (pattern_ptr[1] == '[') |
| | | && (pattern_ptr[2] == ':')) { |
| | | pattern_ptr = pattern_ptr + 3; // for skip ']' in "][:" , pattern_ptr continue, while cmd_ptr remain unchanged |
| | | rightFlag++; |
| | | } else { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Compose command from previsou command anc current command |
| | | * |
| | | * @param ptr_prev pointer to previous command |
| | | * @param len_prev length of previous command |
| | | * @param pptr pointer to pointer of current command |
| | | * @param plen pointer to length of current command |
| | | * |
| | | * ptr_prev and ptr should be in the same memory buffer |
| | | * |
| | | * Function will add part of previous command prior to ptr_prev |
| | | * |
| | | * char * cmd = "meas:volt:dc?;ac?" |
| | | * char * ptr_prev = cmd; |
| | | * size_t len_prev = 13; |
| | | * char * ptr = cmd + 14; |
| | | * size_t len = 3; |
| | | * |
| | | * composeCompoundCommand(ptr_prev, len_prev, &ptr, &len); |
| | | * |
| | | * after calling this |
| | | * |
| | | * |
| | | * |
| | | */ |
| | | scpi_bool_t composeCompoundCommand(char * ptr_prev, size_t len_prev, |
| | | char ** pptr, size_t * plen) { |
| | | char * ptr; |
| | | size_t len; |
| | | size_t i; |
| | | |
| | | /* Invalid input */ |
| | | if (pptr == NULL || plen == NULL) |
| | | return FALSE; |
| | | |
| | | /* no previous command - nothing to do*/ |
| | | if (ptr_prev == NULL || len_prev == 0) |
| | | return TRUE; |
| | | |
| | | ptr = *pptr; |
| | | len = *plen; |
| | | |
| | | /* No current command */ |
| | | if (len == 0 || ptr == NULL) |
| | | return FALSE; |
| | | |
| | | /* Common command or command root - nothing to do */ |
| | | if (ptr[0] == '*' || ptr[0] == ':') |
| | | return TRUE; |
| | | |
| | | /* Previsou command was common command - nothing to do */ |
| | | if (ptr_prev[0] == '*') |
| | | return TRUE; |
| | | |
| | | /* Find last occurence of ':' */ |
| | | for (i = len_prev; i > 0; i--) { |
| | | if (ptr_prev[i-1] == ':') { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | /* Previous command was simple command - nothing to do*/ |
| | | if (i == 0) |
| | | return TRUE; |
| | | |
| | | ptr -= i; |
| | | len += i; |
| | | memmove(ptr, ptr_prev, i); |
| | | *plen = len; |
| | | *pptr = ptr; |
| | | return TRUE; |
| | | } |
| | | |
| | | |
| | | |
| | | #if !HAVE_STRNLEN |
| | |
| | | * All rights reserved. |
| | | */ |
| | | size_t |
| | | BSD_strnlen(const char *s, size_t maxlen) |
| | | { |
| | | BSD_strnlen(const char *s, size_t maxlen) { |
| | | size_t len; |
| | | |
| | | for (len = 0; len < maxlen; len++, s++) { |
| | |
| | | } |
| | | #endif |
| | | |
| | | #if !HAVE_STRNCASECMP && !HAVE_STRNICMP |
| | | int OUR_strncasecmp(const char *s1, const char *s2, size_t n) { |
| | | unsigned char c1, c2; |
| | | |
| | | for(; n != 0; n--) { |
| | | c1 = tolower((unsigned char)*s1++); |
| | | c2 = tolower((unsigned char)*s2++); |
| | | if (c1 != c2) { |
| | | return c1 - c2; |
| | | } |
| | | if (c1 = '\0') { |
| | | return 0; |
| | | } |
| | | } |
| | | return 0; |
| | | } |
| | | #endif |
| | | |
New file |
| | |
| | | /* |
| | | * File: test_lib.c |
| | | * Author: Jan Breuer |
| | | * |
| | | * Created on 2013-11-08 11:49:53 |
| | | */ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include "CUnit/Basic.h" |
| | | |
| | | #include "scpi/scpi.h" |
| | | |
| | | /* |
| | | * CUnit Test Suite |
| | | */ |
| | | |
| | | static const scpi_command_t scpi_commands[] = { |
| | | /* IEEE Mandated Commands (SCPI std V1999.0 4.1.1) */ |
| | | { .pattern = "*CLS", .callback = SCPI_CoreCls,}, |
| | | { .pattern = "*ESE", .callback = SCPI_CoreEse,}, |
| | | { .pattern = "*ESE?", .callback = SCPI_CoreEseQ,}, |
| | | { .pattern = "*ESR?", .callback = SCPI_CoreEsrQ,}, |
| | | { .pattern = "*IDN?", .callback = SCPI_CoreIdnQ,}, |
| | | { .pattern = "*OPC", .callback = SCPI_CoreOpc,}, |
| | | { .pattern = "*OPC?", .callback = SCPI_CoreOpcQ,}, |
| | | { .pattern = "*RST", .callback = SCPI_CoreRst,}, |
| | | { .pattern = "*SRE", .callback = SCPI_CoreSre,}, |
| | | { .pattern = "*SRE?", .callback = SCPI_CoreSreQ,}, |
| | | { .pattern = "*STB?", .callback = SCPI_CoreStbQ,}, |
| | | { .pattern = "*TST?", .callback = SCPI_CoreTstQ,}, |
| | | { .pattern = "*WAI", .callback = SCPI_CoreWai,}, |
| | | |
| | | /* Required SCPI commands (SCPI std V1999.0 4.2.1) */ |
| | | {.pattern = "SYSTem:ERRor[:NEXT]?", .callback = SCPI_SystemErrorNextQ,}, |
| | | {.pattern = "SYSTem:ERRor:COUNt?", .callback = SCPI_SystemErrorCountQ,}, |
| | | {.pattern = "SYSTem:VERSion?", .callback = SCPI_SystemVersionQ,}, |
| | | |
| | | {.pattern = "STATus:QUEStionable[:EVENt]?", .callback = SCPI_StatusQuestionableEventQ,}, |
| | | {.pattern = "STATus:QUEStionable:ENABle", .callback = SCPI_StatusQuestionableEnable,}, |
| | | {.pattern = "STATus:QUEStionable:ENABle?", .callback = SCPI_StatusQuestionableEnableQ,}, |
| | | |
| | | {.pattern = "STATus:PRESet", .callback = SCPI_StatusPreset,}, |
| | | |
| | | SCPI_CMD_LIST_END |
| | | }; |
| | | |
| | | |
| | | char output_buffer[1024]; |
| | | size_t output_buffer_pos = 0; |
| | | |
| | | int_fast16_t err_buffer[128]; |
| | | size_t err_buffer_pos = 0; |
| | | |
| | | static void output_buffer_clear(void) { |
| | | output_buffer[0] = '\0'; |
| | | output_buffer_pos = 0; |
| | | } |
| | | |
| | | static size_t output_buffer_write(const char * data, size_t len) { |
| | | memcpy(output_buffer + output_buffer_pos, data, len); |
| | | output_buffer_pos += len; |
| | | output_buffer[output_buffer_pos] = '\0'; |
| | | } |
| | | |
| | | scpi_t scpi_context; |
| | | static void error_buffer_clear(void) { |
| | | err_buffer[0] = '\0'; |
| | | err_buffer_pos = 0; |
| | | |
| | | SCPI_EventClear(&scpi_context); |
| | | SCPI_ErrorClear(&scpi_context); |
| | | } |
| | | |
| | | static void error_buffer_add(int_fast16_t err) { |
| | | err_buffer[err_buffer_pos] = err; |
| | | err_buffer_pos++; |
| | | } |
| | | |
| | | |
| | | static size_t SCPI_Write(scpi_t * context, const char * data, size_t len) { |
| | | (void) context; |
| | | |
| | | return output_buffer_write(data, len); |
| | | } |
| | | |
| | | static scpi_result_t SCPI_Flush(scpi_t * context) { |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | static int SCPI_Error(scpi_t * context, int_fast16_t err) { |
| | | (void) context; |
| | | |
| | | error_buffer_add(err); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | scpi_reg_val_t srq_val = 0; |
| | | static scpi_result_t SCPI_Control(scpi_t * context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val) { |
| | | if (SCPI_CTRL_SRQ == ctrl) { |
| | | srq_val = val; |
| | | } else { |
| | | fprintf(stderr, "**CTRL %02x: 0x%X (%d)\r\n", ctrl, val, val); |
| | | } |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | scpi_bool_t TST_executed = FALSE; |
| | | scpi_bool_t RST_executed = FALSE; |
| | | static scpi_result_t SCPI_Test(scpi_t * context) { |
| | | TST_executed = TRUE; |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | static scpi_result_t SCPI_Reset(scpi_t * context) { |
| | | RST_executed = TRUE; |
| | | return SCPI_RES_OK; |
| | | } |
| | | |
| | | static scpi_interface_t scpi_interface = { |
| | | .error = SCPI_Error, |
| | | .write = SCPI_Write, |
| | | .control = SCPI_Control, |
| | | .flush = SCPI_Flush, |
| | | .reset = SCPI_Reset, |
| | | .test = SCPI_Test, |
| | | }; |
| | | |
| | | #define SCPI_INPUT_BUFFER_LENGTH 256 |
| | | static char scpi_input_buffer[SCPI_INPUT_BUFFER_LENGTH]; |
| | | |
| | | static scpi_reg_val_t scpi_regs[SCPI_REG_COUNT]; |
| | | |
| | | |
| | | scpi_t scpi_context = { |
| | | .cmdlist = scpi_commands, |
| | | .buffer = { |
| | | .length = SCPI_INPUT_BUFFER_LENGTH, |
| | | .data = scpi_input_buffer, |
| | | }, |
| | | .interface = &scpi_interface, |
| | | .registers = scpi_regs, |
| | | .units = scpi_units_def, |
| | | .special_numbers = scpi_special_numbers_def, |
| | | .idn = {"MA", "IN", NULL, "VER"}, |
| | | }; |
| | | |
| | | |
| | | int init_suite(void) { |
| | | SCPI_Init(&scpi_context); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int clean_suite(void) { |
| | | return 0; |
| | | } |
| | | |
| | | void testCommandsHandling(void) { |
| | | #define TEST_INPUT(data, output) { \ |
| | | SCPI_Input(&scpi_context, data, strlen(data)); \ |
| | | CU_ASSERT_STRING_EQUAL(output, output_buffer); \ |
| | | } |
| | | output_buffer_clear(); |
| | | |
| | | /* Test single command */ |
| | | TEST_INPUT("*IDN?\r\n", "MA, IN, 0, VER\r\n"); |
| | | output_buffer_clear(); |
| | | |
| | | /* Test multiple commands in input buffer */ |
| | | TEST_INPUT("*IDN?\r\n*IDN?\r\n*IDN?\r\n*IDN?\r\n", "MA, IN, 0, VER\r\nMA, IN, 0, VER\r\nMA, IN, 0, VER\r\nMA, IN, 0, VER\r\n"); |
| | | output_buffer_clear(); |
| | | |
| | | /* Test one command in multiple buffers */ |
| | | TEST_INPUT("*IDN?", ""); |
| | | TEST_INPUT("\r\n", "MA, IN, 0, VER\r\n"); |
| | | output_buffer_clear(); |
| | | |
| | | /* Test input "timeout" - input with length == 0 */ |
| | | TEST_INPUT("*IDN?", ""); |
| | | TEST_INPUT("", "MA, IN, 0, VER\r\n"); |
| | | output_buffer_clear(); |
| | | |
| | | CU_ASSERT_EQUAL(err_buffer_pos, 0); |
| | | error_buffer_clear(); |
| | | |
| | | // TODO: Compound commands A:B;C -> A:B; A:C |
| | | } |
| | | |
| | | void testErrorHandling(void) { |
| | | output_buffer_clear(); |
| | | error_buffer_clear(); |
| | | |
| | | #define TEST_ERROR(data, output, err_num) { \ |
| | | SCPI_Input(&scpi_context, data, strlen(data)); \ |
| | | CU_ASSERT_STRING_EQUAL(output, output_buffer); \ |
| | | error_buffer_clear(); \ |
| | | } |
| | | |
| | | TEST_ERROR("*IDN?\r\n", "MA, IN, 0, VER\r\n", 0); |
| | | output_buffer_clear(); |
| | | TEST_ERROR("IDN?\r\n", "", SCPI_ERROR_UNDEFINED_HEADER); |
| | | TEST_ERROR("*ESE\r\n", "", SCPI_ERROR_MISSING_PARAMETER); |
| | | TEST_ERROR("*IDN? 12\r\n", "MA, IN, 0, VER\r\n", SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | output_buffer_clear(); |
| | | |
| | | // TODO: SCPI_ERROR_INVALID_SEPARATOR |
| | | // TODO: SCPI_ERROR_INVALID_SUFFIX |
| | | // TODO: SCPI_ERROR_SUFFIX_NOT_ALLOWED |
| | | // TODO: SCPI_ERROR_EXECUTION_ERROR |
| | | // TODO: SCPI_ERROR_ILLEGAL_PARAMETER_VALUE |
| | | } |
| | | |
| | | void testIEEE4882(void) { |
| | | #define TEST_IEEE4882(data, output) { \ |
| | | SCPI_Input(&scpi_context, data, strlen(data)); \ |
| | | CU_ASSERT_STRING_EQUAL(output, output_buffer); \ |
| | | output_buffer_clear(); \ |
| | | } |
| | | |
| | | output_buffer_clear(); |
| | | error_buffer_clear(); |
| | | |
| | | TEST_IEEE4882("*CLS\r\n", ""); |
| | | TEST_IEEE4882("*ESE 0x20\r\n", ""); |
| | | TEST_IEEE4882("*ESE?\r\n", "32\r\n"); |
| | | TEST_IEEE4882("*ESR?\r\n", "0\r\n"); |
| | | TEST_IEEE4882("*IDN?\r\n", "MA, IN, 0, VER\r\n"); |
| | | TEST_IEEE4882("*OPC\r\n", ""); |
| | | TEST_IEEE4882("*OPC?\r\n", "1\r\n"); |
| | | |
| | | TEST_IEEE4882("*SRE 0xFF\r\n", ""); |
| | | TEST_IEEE4882("*SRE?\r\n", "255\r\n"); |
| | | TEST_IEEE4882("*STB?\r\n", "0\r\n"); |
| | | TEST_IEEE4882("*ESR?\r\n", "1\r\n"); |
| | | |
| | | srq_val = 0; |
| | | TEST_IEEE4882("ABCD\r\n", ""); /* "Undefined header" cause command error */ |
| | | CU_ASSERT_EQUAL(srq_val, 96); /* value of STB as service request */ |
| | | TEST_IEEE4882("*STB?\r\n", "96\r\n"); /* Event status register + Service request */ |
| | | TEST_IEEE4882("*ESR?\r\n", "32\r\n"); /* Command error */ |
| | | |
| | | TEST_IEEE4882("*STB?\r\n", "0\r\n"); |
| | | TEST_IEEE4882("*ESR?\r\n", "0\r\n"); |
| | | |
| | | TEST_IEEE4882("SYST:ERR:NEXT?\r\n", "-113, \"Undefined header\"\r\n"); |
| | | TEST_IEEE4882("SYST:ERR:NEXT?\r\n", "0, \"No error\"\r\n"); |
| | | |
| | | RST_executed = FALSE; |
| | | TEST_IEEE4882("*RST\r\n", ""); |
| | | CU_ASSERT_EQUAL(RST_executed, TRUE); |
| | | |
| | | TST_executed = FALSE; |
| | | TEST_IEEE4882("*TST?\r\n", "1\r\n"); |
| | | CU_ASSERT_EQUAL(TST_executed, TRUE); |
| | | |
| | | TEST_IEEE4882("*WAI\r\n", ""); |
| | | |
| | | TEST_IEEE4882("SYSTem:VERSion?\r\n", "1999.0\r\n"); |
| | | } |
| | | |
| | | void testParameters(void) { |
| | | // TODO: test parsin parameters |
| | | |
| | | // TODO: Int |
| | | // TODO: Double |
| | | // TODO: String |
| | | // TODO: Text |
| | | // TODO: Bool |
| | | // TODO: Choice |
| | | } |
| | | |
| | | void testResults(void) { |
| | | // TODO: test producing results |
| | | |
| | | // TODO: String |
| | | // TODO: Int |
| | | // TODO: Double |
| | | // TODO: Text |
| | | // TODO: Bool |
| | | } |
| | | |
| | | int main() { |
| | | CU_pSuite pSuite = NULL; |
| | | |
| | | /* Initialize the CUnit test registry */ |
| | | if (CUE_SUCCESS != CU_initialize_registry()) |
| | | return CU_get_error(); |
| | | |
| | | /* Add a suite to the registry */ |
| | | pSuite = CU_add_suite("Parser", init_suite, clean_suite); |
| | | if (NULL == pSuite) { |
| | | CU_cleanup_registry(); |
| | | return CU_get_error(); |
| | | } |
| | | |
| | | /* Add the tests to the suite */ |
| | | if ((NULL == CU_add_test(pSuite, "Commands handling", testCommandsHandling)) || |
| | | (NULL == CU_add_test(pSuite, "Error handling", testErrorHandling)) || |
| | | (NULL == CU_add_test(pSuite, "IEEE 488.2 Mandatory commands", testIEEE4882)) || |
| | | (NULL == CU_add_test(pSuite, "Parameters", testParameters)) || |
| | | (NULL == CU_add_test(pSuite, "Results", testResults)) |
| | | ) { |
| | | CU_cleanup_registry(); |
| | | return CU_get_error(); |
| | | } |
| | | |
| | | /* Run all tests using the CUnit Basic interface */ |
| | | CU_basic_set_mode(CU_BRM_VERBOSE); |
| | | CU_basic_run_tests(); |
| | | CU_cleanup_registry(); |
| | | return CU_get_error(); |
| | | } |
| | |
| | | /*- |
| | | * Copyright (c) 2013 Jan Breuer |
| | | * Richard.hmm |
| | | * Copyright (c) 2012 Jan Breuer |
| | | * |
| | | * All Rights Reserved |
| | | * |
| | | * Redistribution and use in source and binary forms, with or without |
| | | * modification, are permitted provided that the following conditions are |
| | | * met: |
| | | * 1. Redistributions of source code must retain the above copyright notice, |
| | | * this list of conditions and the following disclaimer. |
| | | * 2. Redistributions in binary form must reproduce the above copyright |
| | | * notice, this list of conditions and the following disclaimer in the |
| | | * documentation and/or other materials provided with the distribution. |
| | | * |
| | | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR |
| | | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| | | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| | | * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE |
| | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| | | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| | | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| | | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| | | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| | | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| | | */ |
| | | |
| | | /* |
| | | * File: test_scpi_utils.c |
| | | * Author: Jan Breuer |
| | |
| | | #include "CUnit/Basic.h" |
| | | |
| | | #include "scpi/scpi.h" |
| | | #include "../src/utils.h" |
| | | #include "scpi/utils_private.h" |
| | | |
| | | /* |
| | | * CUnit Test Suite |
| | |
| | | CU_ASSERT_FALSE(compareStr("ABCD", 4, "abcd", 3)); |
| | | } |
| | | |
| | | void test_compareStrAndNum() { |
| | | |
| | | CU_ASSERT_TRUE(compareStrAndNum("abcd", 1, "afgh", 1)); |
| | | CU_ASSERT_TRUE(compareStrAndNum("ABCD", 4, "abcd", 4)); |
| | | CU_ASSERT_TRUE(compareStrAndNum("AbCd", 3, "AbCE", 3)); |
| | | CU_ASSERT_TRUE(compareStrAndNum("ABCD", 1, "a", 1)); |
| | | |
| | | CU_ASSERT_FALSE(compareStrAndNum("abcd", 1, "efgh", 1)); |
| | | CU_ASSERT_FALSE(compareStrAndNum("ABCD", 4, "abcd", 3)); |
| | | |
| | | CU_ASSERT_TRUE(compareStrAndNum("abcd", 4, "abcd1", 5)); |
| | | CU_ASSERT_TRUE(compareStrAndNum("abcd", 4, "abcd123", 7)); |
| | | CU_ASSERT_FALSE(compareStrAndNum("abcd", 4, "abcd12A", 7)); |
| | | CU_ASSERT_FALSE(compareStrAndNum("abcd", 4, "abcdB12", 7)); |
| | | CU_ASSERT_FALSE(compareStrAndNum("abdd", 4, "abcd132", 7)); |
| | | |
| | | } |
| | | |
| | | void test_locateText() { |
| | | |
| | | const char * v; |
| | |
| | | } |
| | | |
| | | void test_matchPattern() { |
| | | bool_t result; |
| | | scpi_bool_t result; |
| | | |
| | | #define TEST_MATCH_PATTERN(p, s, r) \ |
| | | do { \ |
| | |
| | | } |
| | | |
| | | void test_matchCommand() { |
| | | bool_t result; |
| | | scpi_bool_t result; |
| | | |
| | | #define TEST_MATCH_COMMAND(p, s, r) \ |
| | | do { \ |
| | |
| | | TEST_MATCH_COMMAND("ABc:AACddd", ":abc:aacddd", TRUE); |
| | | TEST_MATCH_COMMAND("ABc:AACddd", ":abc:aacdd", FALSE); |
| | | TEST_MATCH_COMMAND("ABc:AACddd", ":a:aac", FALSE); |
| | | TEST_MATCH_COMMAND("?", "?", TRUE); |
| | | TEST_MATCH_COMMAND("A?", "A?", TRUE); |
| | | TEST_MATCH_COMMAND("A", "A?", FALSE); |
| | | TEST_MATCH_COMMAND("A?", "A", FALSE); |
| | | TEST_MATCH_COMMAND("[:ABc]:AACddd", ":ab:aac", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:AACddd", "aac", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:AACddd", "aac?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:AACddd?", ":ab:aac?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:AACddd?", "aac?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:AACddd?", "aac", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd]:CDe", "ab:bc:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd]:CDe", "ab:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd]:CDe", "ab:cd?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd]:CDe?", "ab:bc:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd]:CDe?", "ab:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd]:CDe?", "ab:cd", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc:BCd[:CDe]", "ab:bc:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc:BCd[:CDe]", "ab:bc", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc:BCd[:CDe]", "ab:bc?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc:BCd[:CDe]?", "ab:bc:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc:BCd[:CDe]?", "ab:bc?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc:BCd[:CDe]?", "ab:bc", FALSE); // test optional keyword |
| | | |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]", "ab:bc:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]", "ab:bc", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]", "bc:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]", "ab:bc?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]", "bc:cd?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]?", "ab:bc:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]?", "ab:bc?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]?", "bc:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]?", "ab:bc", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("[:ABc]:BCd[:CDe]?", "bc:cd", FALSE); // test optional keyword |
| | | |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]", "ab:bc:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]", "ab:bc", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]", "ab:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]", "ab:bc?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]", "ab:cd?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]?", "ab:bc:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]?", "ab:bc?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]?", "ab:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]?", "ab:bc", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe]?", "ab:cd", FALSE); // test optional keyword |
| | | |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:bc:cd:de", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:bc:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:bc:de", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:cd:de", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:bc", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:cd", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:de", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:bc:cd?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:bc:de?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:cd:de?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:bc?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:cd?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab:de?", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]", "ab?", FALSE); // test optional keyword |
| | | |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:bc:cd:de?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:bc:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:bc:de?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:cd:de?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:bc?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:cd?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:de?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab?", TRUE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:bc:cd", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:bc:de", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:cd:de", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:bc", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:cd", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab:de", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("ABc[:BCd][:CDe][:DEf]?", "ab", FALSE); // test optional keyword |
| | | TEST_MATCH_COMMAND("*IDN?", "idn", FALSE); // common command |
| | | TEST_MATCH_COMMAND("*IDN?", "idn?", FALSE); // common command |
| | | TEST_MATCH_COMMAND("*IDN?", "*idn", FALSE); // common command |
| | | TEST_MATCH_COMMAND("*IDN?", "*idn?", TRUE); // common command |
| | | TEST_MATCH_COMMAND("*IDN?", ":idn", FALSE); // common command |
| | | TEST_MATCH_COMMAND("*IDN?", ":idn?", FALSE); // common command |
| | | TEST_MATCH_COMMAND("*IDN?", ":*idn", FALSE); // common command |
| | | TEST_MATCH_COMMAND("*IDN?", ":*idn?", FALSE); // common command |
| | | |
| | | TEST_MATCH_COMMAND("ABCdef#", "abc", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("ABCdef#", "abc1324", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("ABCdef#", "abcDef1324", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("ABCdef#", "abcDef124b", FALSE); // test numeric parameter |
| | | |
| | | TEST_MATCH_COMMAND("OUTPut#:MODulation#:FM#", "abc", FALSE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#:MODulation#:FM#", "outp1:mod10:fm", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#:MODulation#:FM#", "output1:mod10:fm", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#:MODulation#:FM#", "outp1:modulation:fm5", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#:MODulation#:FM#", "output:mod:fm", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#:MODulation#:FM#", "outp1:mod10a:fm", FALSE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#[:MODulation#]:FM#", "outp1:fm", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#[:MODulation#]:FM#", "outp1:mod10:fm", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#[:MODulation#]:FM#", "outp1:fm2", TRUE); // test numeric parameter |
| | | TEST_MATCH_COMMAND("OUTPut#[:MODulation#]:FM#", "output:fm", TRUE); // test numeric parameter |
| | | } |
| | | |
| | | void test_composeCompoundCommand(void) { |
| | | |
| | | #define TEST_COMPOSE_COMMAND(b, c1_len, c2_pos, c2_len, c2_final, r) \ |
| | | { \ |
| | | char buffer[100]; \ |
| | | char * cmd_prev = buffer; \ |
| | | char * cmd = buffer + c2_pos; \ |
| | | size_t len_prev = c1_len; \ |
| | | size_t len = c2_len; \ |
| | | scpi_bool_t res; \ |
| | | \ |
| | | strcpy(buffer, b); \ |
| | | res = composeCompoundCommand(cmd_prev, len_prev, &cmd, &len); \ |
| | | CU_ASSERT_EQUAL(res, r); \ |
| | | CU_ASSERT_EQUAL(len, strlen(c2_final)); \ |
| | | CU_ASSERT_STRING_EQUAL(cmd, c2_final); \ |
| | | }\ |
| | | |
| | | TEST_COMPOSE_COMMAND("A:B;C", 3, 4, 1, "A:C", TRUE); |
| | | TEST_COMPOSE_COMMAND("A:B;DD", 3, 4, 2, "A:DD", TRUE); |
| | | TEST_COMPOSE_COMMAND("A:B", 0, 0, 3, "A:B", TRUE); |
| | | TEST_COMPOSE_COMMAND("*IDN? ; ABC", 5, 8, 3, "ABC", TRUE); |
| | | TEST_COMPOSE_COMMAND("A:B;*IDN?", 3, 4, 5, "*IDN?", TRUE); |
| | | TEST_COMPOSE_COMMAND("A:B;:C", 3, 4, 2, ":C", TRUE); |
| | | TEST_COMPOSE_COMMAND("B;C", 1, 2, 1, "C", TRUE); |
| | | TEST_COMPOSE_COMMAND("A:B;C:D", 3, 4, 3, "A:C:D", TRUE); |
| | | TEST_COMPOSE_COMMAND(":A:B;C", 4, 5, 1, ":A:C", TRUE); |
| | | TEST_COMPOSE_COMMAND(":A:B;:C", 4, 5, 2, ":C", TRUE); |
| | | TEST_COMPOSE_COMMAND(":A;C", 2, 3, 1, ":C", TRUE); |
| | | |
| | | scpi_bool_t composeCompoundCommand(char * ptr_prev, size_t len_prev, char ** pptr, size_t * plen); |
| | | |
| | | } |
| | | |
| | | int main() { |
| | |
| | | || (NULL == CU_add_test(pSuite, "strToLong", test_strToLong)) |
| | | || (NULL == CU_add_test(pSuite, "strToDouble", test_strToDouble)) |
| | | || (NULL == CU_add_test(pSuite, "compareStr", test_compareStr)) |
| | | || (NULL == CU_add_test(pSuite, "compareStrAndNum", test_compareStrAndNum)) |
| | | || (NULL == CU_add_test(pSuite, "locateText", test_locateText)) |
| | | || (NULL == CU_add_test(pSuite, "locateStr", test_locateStr)) |
| | | || (NULL == CU_add_test(pSuite, "matchPattern", test_matchPattern)) |
| | | || (NULL == CU_add_test(pSuite, "matchCommand", test_matchCommand)) |
| | | || (NULL == CU_add_test(pSuite, "composeCompoundCommand", test_composeCompoundCommand)) |
| | | ) { |
| | | CU_cleanup_registry(); |
| | | return CU_get_error(); |