diff --git a/main.c b/main.c index bb723fc..616c6b3 100644 --- a/main.c +++ b/main.c @@ -5,7 +5,6 @@ #include "scpi_errors.h" - void cmd_aIDNq_cb(const SCPI_argval_t *args) { printf("cb *IDN?\n"); @@ -61,7 +60,7 @@ void cmd_STQE_cb(const SCPI_argval_t *args) printf("STATUS:QUEUE:ENABLE %d\n", args[0].BOOL); } -const SCPI_command_t scpi_cmd_lang[] = { +const SCPI_command_t scpi_commands[] = { { .levels = {"*IDN?"}, .params = {}, @@ -114,20 +113,42 @@ const SCPI_command_t scpi_cmd_lang[] = { int main() { + char buf[256]; + // const char *inp = "*IDN?\n"; // const char *inp = "FREQ 50\n"; - const char *inp = "DISP:TEXT 'ban\\\\ana', OFF\nDISP:TEXT \"dblquot!\", 1\r\nFREQ 50\r\n"; +// const char *inp = "DISP:TEXT 'ban\\\\ana', OFF\nDISP:TEXT \"dblquot!\", 1\r\nFREQ 50\r\n"; -// const char *inp = "DATA:BLOB 13.456, #216AbcdEfghIjklMnop\nFREQ 50\r\n"; -// const char *inp = "STAT:QUE:ENAB?;ENAB \t 1;ENAB?;:*IDN?\n"; + const char *inp = "FOO\nDATA:BLOB 13.456, #216AbcdEfghIjklMnop\nFREQ 50\r\n"; + //const char *inp = "STAT:QUE:ENAB?;ENAB \t 1;ENAB?;:*IDN?\n"; for (int i = 0; i < strlen(inp); i++) { scpi_handle_byte(inp[i]); } - char buf[250]; + scpi_error_string(buf, E_EXE_DATA_QUESTIONABLE, "The data smells fishy"); - printf("%s\n", buf); +// scpi_read_error(buf), printf("%s\n", buf); + +// scpi_add_error(E_CMD_BLOCK_DATA_ERROR, NULL); + +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_add_error(E_CMD_DATA_TYPE_ERROR, "LOL OOPS"); +// scpi_add_error(E_CMD_CHARACTER_DATA_TOO_LONG, "Too long data very overflow such fail"); +// scpi_add_error(E_CMD_MACRO_PARAMETER_ERROR, "maaaacrooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"); +// scpi_add_error(E_CMD_EXPONENT_TOO_LARGE, NULL); +// scpi_add_error(E_DEV_CALIBRATION_FAILED, NULL); +// scpi_add_error(E_DEV_MEMORY_ERROR, NULL); +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_add_error(E_DEV_MEMORY_ERROR, "lel"); +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_read_error(buf), printf("%s\n", buf); +// scpi_read_error(buf), printf("%s\n", buf); } diff --git a/scpi.pro.user b/scpi.pro.user index af18b34..f59e021 100644 --- a/scpi.pro.user +++ b/scpi.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/scpi_errors.c b/scpi_errors.c index 973a845..cfcdf7c 100644 --- a/scpi_errors.c +++ b/scpi_errors.c @@ -12,6 +12,7 @@ typedef struct { } SCPI_error_desc; static const SCPI_error_desc error_table[] = { + { 0, "No error"}, { -100, "Command error"}, { -101, "Invalid character"}, { -102, "Syntax error"}, @@ -133,13 +134,13 @@ static const SCPI_error_desc error_table[] = { { -600, "User request"}, { -700, "Request control"}, { -800, "Operation complete"}, - {0} + { -9999} // end mark }; const char * scpi_error_message(SCPI_error_t errno) { - for (int i = 0; error_table[i].errno != 0; i++) { + for (int i = 0; error_table[i].errno != -9999; i++) { if (error_table[i].errno == errno) { return error_table[i].msg; } @@ -159,9 +160,7 @@ void scpi_error_string(char *buffer, SCPI_error_t errno, const char *extra) len = offs = sprintf(buffer, "%d,\"", errno); // ," buffer += offs; - if (msg == NULL) { - msg = "Unknown error"; - } + if (msg == NULL) msg = "Unknown error"; offs = sprintf(buffer, "%s", msg); // Error message len += offs; @@ -169,7 +168,7 @@ void scpi_error_string(char *buffer, SCPI_error_t errno, const char *extra) if (extra != NULL) { // extra info - offs = sprintf(buffer, "; "); // ;_ + offs = sprintf(buffer, ";"); // ; len += offs; buffer += offs; diff --git a/scpi_errors.h b/scpi_errors.h index 1b86abe..5b83078 100644 --- a/scpi_errors.h +++ b/scpi_errors.h @@ -4,6 +4,7 @@ // SCPI error constants typedef enum { + E_NO_ERROR = 0, E_COMMAND_ERROR = -100, E_CMD_INVALID_CHARACTER = -101, E_CMD_SYNTAX_ERROR = -102, diff --git a/scpi_parser.c b/scpi_parser.c index a4fd813..f11e50c 100644 --- a/scpi_parser.c +++ b/scpi_parser.c @@ -1,11 +1,38 @@ #include #include #include -#include "scpi_parser.h" #include #include +#include "scpi_parser.h" +#include "scpi_errors.h" + +// Config +#define ERR_QUEUE_LEN 4 +#define MAX_ERROR_LEN 255 +#define MAX_CHARBUF_LEN 255 + + +// Char matching +#define INRANGE(c, a, b) ((c) >= (a) && (c) <= (b)) +#define IS_WHITESPACE(c) (INRANGE((c), 0, 9) || INRANGE((c), 11, 32)) + +#define IS_LCASE_CHAR(c) INRANGE((c), 'a', 'z') +#define IS_UCASE_CHAR(c) INRANGE((c), 'A', 'Z') +#define IS_NUMBER_CHAR(c) INRANGE((c), '0', '9') +#define IS_MULTIPLIER_CHAR(c) ((c) == 'k' || (c) == 'M' || (c) == 'G' || (c) == 'm' || (c) == 'u' || (c) == 'n' || (c) == 'p') + +#define IS_IDENT_CHAR(c) (IS_LCASE_CHAR((c)) || IS_UCASE_CHAR((c)) || IS_NUMBER_CHAR((c)) || (c) == '_' || (c) == '*' || (c) == '?') +#define IS_INT_CHAR(c) (IS_NUMBER_CHAR((c)) || (c) == '-' || (c) == '+' || IS_MULTIPLIER_CHAR((c))) +#define IS_FLOAT_CHAR(c) (IS_NUMBER_CHAR((c)) || (c) == '.' || (c) == 'e' || (c) == 'E' || (c) == '+' || (c) == '-' || IS_MULTIPLIER_CHAR((c))) + +#define CHAR_TO_LOWER(ucase) ((ucase) + 32) +#define CHAR_TO_UPPER(lcase) ((lcase) - 32) + + + +/** Parser internal state enum */ typedef enum { PARS_COMMAND = 0, // collect generic arg, terminated with comma or newline. Leading and trailing whitespace ignored. @@ -19,17 +46,19 @@ typedef enum { PARS_DISCARD_LINE, // used after detecting error - drop all chars until \n } parser_state_t; -#define MAX_CHARBUF_LEN 256 -/** parser internal state struct */ + +/** Parser internal state struct */ static struct { - char err_queue[ERR_QUEUE_LEN][MAX_ERROR_LEN]; - uint8_t err_queue_i; + char err_queue[ERR_QUEUE_LEN][MAX_ERROR_LEN + 1]; + int8_t err_queue_r; + int8_t err_queue_w; + int8_t err_queue_used; // signed for backtracking parser_state_t state; // current parser internal state // string buffer, chars collected here until recognized - char charbuf[MAX_CHARBUF_LEN]; + char charbuf[MAX_CHARBUF_LEN + 1]; uint16_t charbuf_i; int32_t blob_cnt; // preamble counter, if 0, was just #, must read count. Used also for blob body. @@ -39,146 +68,125 @@ static struct { bool string_escape; // last char was backslash, next quote is literal // recognized complete command level strings (FUNCtion) - exact copy from command struct - char cur_levels[MAX_LEVEL_COUNT][MAX_CMD_LEN]; + char cur_levels[SCPI_MAX_LEVEL_COUNT][SCPI_MAX_CMD_LEN]; uint8_t cur_level_i; // next free level slot index bool cmdbuf_kept; // set to 1 after semicolon - cur_levels is kept (removed last part) const SCPI_command_t * matched_cmd; // command is put here after recognition, used as reference for args - SCPI_argval_t args[MAX_PARAM_COUNT]; + SCPI_argval_t args[SCPI_MAX_PARAM_COUNT]; uint8_t arg_i; // next free argument slot index } pst = {0}; // initialized by all zeros + +// ---------------- PRIVATE PROTOTYPES ------------------ + +// Command parsing static void pars_cmd_colon(void); // colon starting a command sub-segment static void pars_cmd_space(void); // space ending a command static void pars_cmd_newline(void); // LF static void pars_cmd_semicolon(void); // semicolon right after a command -static bool pars_match_cmd(bool partial); + +// Command properties (find length of array) +static uint8_t cmd_param_count(const SCPI_command_t *cmd); +static uint8_t cmd_level_count(const SCPI_command_t *cmd); + +static bool match_cmd(bool partial); +static bool match_any_cmd_from_array(const SCPI_command_t arr[], bool partial); +static bool match_cmd_do(const SCPI_command_t *cmd, bool partial); +static void run_command_callback(void); + +// Argument parsing static void pars_arg_char(char c); static void pars_arg_comma(void); static void pars_arg_newline(void); static void pars_arg_semicolon(void); static void pars_blob_preamble_char(uint8_t c); +static void arg_convert_value(void); -static uint8_t cmd_param_count(const SCPI_command_t *cmd); -static uint8_t cmd_level_count(const SCPI_command_t *cmd); - -static bool try_match_cmd(const SCPI_command_t *cmd, bool partial); static void charbuf_terminate(void); static void charbuf_append(char c); -static void pars_run_callback(void); -static void arg_convert_value(void); - -// char matching -#define INRANGE(c, a, b) ((c) >= (a) && (c) <= (b)) -#define IS_WHITESPACE(c) (INRANGE((c), 0, 9) || INRANGE((c), 11, 32)) - -#define IS_LCASE_CHAR(c) INRANGE((c), 'a', 'z') -#define IS_UCASE_CHAR(c) INRANGE((c), 'A', 'Z') -#define IS_NUMBER_CHAR(c) INRANGE((c), '0', '9') -#define IS_MULTIPLIER_CHAR(c) ((c) == 'k' || (c) == 'M' || (c) == 'G' || (c) == 'm' || (c) == 'u' || (c) == 'n' || (c) == 'p') -#define IS_IDENT_CHAR(c) (IS_LCASE_CHAR((c)) || IS_UCASE_CHAR((c)) || IS_NUMBER_CHAR((c)) || (c) == '_' || (c) == '*' || (c) == '?') -#define IS_INT_CHAR(c) (IS_NUMBER_CHAR((c)) || (c) == '-' || (c) == '+' || IS_MULTIPLIER_CHAR((c))) -#define IS_FLOAT_CHAR(c) (IS_NUMBER_CHAR((c)) || (c) == '.' || (c) == 'e' || (c) == 'E' || (c) == '+' || (c) == '-' || IS_MULTIPLIER_CHAR((c))) +// Reset +static void pars_reset_cmd(void); +static void pars_reset_cmd_keeplevel(void); -#define CHAR_TO_LOWER(ucase) ((ucase) + 32) -#define CHAR_TO_UPPER(lcase) ((lcase) - 32) +// ---------------- BUILTIN SCPI COMMANDS ------------------ -/** Reset parser state. */ -static void pars_reset_cmd(void) +static void builtin_cb_FOO(const SCPI_argval_t *args) { - pst.state = PARS_COMMAND; - pst.charbuf_i = 0; - pst.cur_level_i = 0; - pst.cmdbuf_kept = false; - pst.matched_cmd = NULL; - pst.arg_i = 0; - pst.string_escape = false; + printf("Builtin FOO\n"); } -/** Reset parser state, keep level (semicolon) */ -static void pars_reset_cmd_keeplevel(void) -{ - pst.state = PARS_COMMAND; - pst.charbuf_i = 0; +const SCPI_command_t scpi_commands_builtin[] = { + { + .levels = {"FOO"}, + .params = {}, + .callback = builtin_cb_FOO + }, + {0} // end marker +}; - // rewind to last colon - if (pst.cur_level_i > 0) { - pst.cur_level_i--; // keep prev levels - } - pst.cmdbuf_kept = true; - pst.matched_cmd = NULL; - pst.arg_i = 0; - pst.string_escape = false; -} +// ------------------- ERROR QUEUE --------------------- -static uint8_t cmd_param_count(const SCPI_command_t *cmd) +void scpi_add_error(SCPI_error_t errno, const char *extra) { - for (uint8_t i = 0; i < MAX_PARAM_COUNT; i++) { - if (cmd->params[i] == SCPI_DT_NONE) { - return i; + bool added = true; + if (pst.err_queue_used >= ERR_QUEUE_LEN) { + errno = E_DEV_QUEUE_OVERFLOW; + extra = NULL; + added = false; // replaced only + + // backtrack + pst.err_queue_w--; + pst.err_queue_used--; + if (pst.err_queue_w < 0) { + pst.err_queue_w = ERR_QUEUE_LEN - 1; } } - return MAX_PARAM_COUNT; -} - + scpi_error_string(pst.err_queue[pst.err_queue_w], errno, extra); -static uint8_t cmd_level_count(const SCPI_command_t *cmd) -{ - for (uint8_t i = 0; i < MAX_LEVEL_COUNT; i++) { - if (cmd->levels[i][0] == 0) { - return i; - } + pst.err_queue_w++; + pst.err_queue_used++; + if (pst.err_queue_w >= ERR_QUEUE_LEN) { + pst.err_queue_w = 0; } - - return MAX_LEVEL_COUNT; } -/** Add a byte to charbuf, error on overflow */ -static void charbuf_append(char c) +void scpi_read_error(char *buf) { - if (pst.charbuf_i >= MAX_CHARBUF_LEN) { - printf("ERROR string buffer overflow.\n");//TODO error - pst.state = PARS_DISCARD_LINE; + if (pst.err_queue_used == 0) { + scpi_error_string(buf, E_NO_ERROR, NULL); + return; } - pst.charbuf[pst.charbuf_i++] = c; -} - + strcpy(buf, pst.err_queue[pst.err_queue_r++]); + pst.err_queue_used--; -/** Terminate charbuf and rewind the pointer to start */ -static void charbuf_terminate(void) -{ - pst.charbuf[pst.charbuf_i] = '\0'; - pst.charbuf_i = 0; + if (pst.err_queue_r >= ERR_QUEUE_LEN) { + pst.err_queue_r = 0; + } } -/** Run the matched command's callback with the arguments */ -static void pars_run_callback(void) +uint8_t scpi_error_count(void) { - if (pst.matched_cmd != NULL) { - pst.matched_cmd->callback(pst.args); // run - } + return pst.err_queue_used; } -void scpi_discard_blob(void) -{ - pst.state = PARS_ARG_BLOB_DISCARD; -} +// ----------------- INPUT PARSING ---------------- void scpi_handle_byte(const uint8_t b) { @@ -192,7 +200,7 @@ void scpi_handle_byte(const uint8_t b) if (IS_IDENT_CHAR(c)) { // valid command char - if (pst.charbuf_i < MAX_CMD_LEN) { + if (pst.charbuf_i < SCPI_MAX_CMD_LEN) { charbuf_append(c); } else { printf("ERROR command part too long.\n");//TODO error @@ -239,8 +247,8 @@ void scpi_handle_byte(const uint8_t b) if (IS_WHITESPACE(c)) break; if (c == '\n') { - if(pst.state != PARS_TRAILING_WHITE_NOCB) { - pars_run_callback(); + if (pst.state != PARS_TRAILING_WHITE_NOCB) { + run_command_callback(); } pars_reset_cmd(); @@ -334,6 +342,105 @@ void scpi_handle_byte(const uint8_t b) } + +// ------------------- RESET INTERNAL STATE ------------------ + + +// public // +/** Discard the rest of the currently processed blob */ +void scpi_discard_blob(void) +{ + if (pst.state == PARS_ARG_BLOB_BODY) { + pst.state = PARS_ARG_BLOB_DISCARD; + } +} + + +/** Reset parser state. */ +static void pars_reset_cmd(void) +{ + pst.state = PARS_COMMAND; + pst.charbuf_i = 0; + pst.cur_level_i = 0; + pst.cmdbuf_kept = false; + pst.matched_cmd = NULL; + pst.arg_i = 0; + pst.string_escape = false; +} + + +/** Reset parser state, keep level (semicolon) */ +static void pars_reset_cmd_keeplevel(void) +{ + pst.state = PARS_COMMAND; + pst.charbuf_i = 0; + + // rewind to last colon + if (pst.cur_level_i > 0) { + pst.cur_level_i--; // keep prev levels + } + + pst.cmdbuf_kept = true; + pst.matched_cmd = NULL; + pst.arg_i = 0; + pst.string_escape = false; +} + + +// ------------------- COMMAND HELPERS ------------------- + +/** Get param count from command struct */ +static uint8_t cmd_param_count(const SCPI_command_t *cmd) +{ + for (uint8_t i = 0; i < SCPI_MAX_PARAM_COUNT; i++) { + if (cmd->params[i] == SCPI_DT_NONE) { + return i; + } + } + + return SCPI_MAX_PARAM_COUNT; +} + + +/** Get level count from command struct */ +static uint8_t cmd_level_count(const SCPI_command_t *cmd) +{ + for (uint8_t i = 0; i < SCPI_MAX_LEVEL_COUNT; i++) { + if (cmd->levels[i][0] == 0) { + return i; + } + } + + return SCPI_MAX_LEVEL_COUNT; +} + + + +// ----------------- CHAR BUFFER HELPERS ------------------- + +/** Add a byte to charbuf, error on overflow */ +static void charbuf_append(char c) +{ + if (pst.charbuf_i >= MAX_CHARBUF_LEN) { + printf("ERROR string buffer overflow.\n");//TODO error + pst.state = PARS_DISCARD_LINE; + } + + pst.charbuf[pst.charbuf_i++] = c; +} + + +/** Terminate charbuf and rewind the pointer to start */ +static void charbuf_terminate(void) +{ + pst.charbuf[pst.charbuf_i] = '\0'; + pst.charbuf_i = 0; +} + + + +// ----------------- PARSING COMMANDS --------------- + /** Colon received when collecting command parts */ static void pars_cmd_colon(void) { @@ -351,7 +458,7 @@ static void pars_cmd_colon(void) } else { // internal colon - partial match - if (pars_match_cmd(true)) { + if (match_cmd(true)) { // ok } else { printf("ERROR no such command: %s\n", pst.charbuf);//TODO error @@ -361,6 +468,7 @@ static void pars_cmd_colon(void) } +/** Semiolon received when collecting command parts */ static void pars_cmd_semicolon(void) { if (pst.cur_level_i == 0 && pst.charbuf_i == 0) { @@ -370,10 +478,10 @@ static void pars_cmd_semicolon(void) return; } - if (pars_match_cmd(false)) { + if (match_cmd(false)) { if (cmd_param_count(pst.matched_cmd) == 0) { // no param command - OK - pars_run_callback(); + run_command_callback(); pars_reset_cmd_keeplevel(); // keep level - that's what semicolon does } else { printf("ERROR command missing arguments.\n");//TODO error @@ -396,10 +504,10 @@ static void pars_cmd_newline(void) } // complete match - if (pars_match_cmd(false)) { + if (match_cmd(false)) { if (cmd_param_count(pst.matched_cmd) == 0) { // no param command - OK - pars_run_callback(); + run_command_callback(); pars_reset_cmd(); } else { printf("ERROR command missing arguments.\n");//TODO error @@ -420,7 +528,7 @@ static void pars_cmd_space(void) return; } - if (pars_match_cmd(false)) { + if (match_cmd(false)) { if (cmd_param_count(pst.matched_cmd) == 0) { // no commands pst.state = PARS_TRAILING_WHITE; @@ -500,7 +608,7 @@ static bool level_str_matches(const char *test, const char *pattern) -static bool pars_match_cmd(bool partial) +static bool match_cmd(bool partial) { charbuf_terminate(); // zero-end and rewind index @@ -508,17 +616,30 @@ static bool pars_match_cmd(bool partial) char *dest = pst.cur_levels[pst.cur_level_i++]; strcpy(dest, pst.charbuf); + + // First try builtin commands + if (match_any_cmd_from_array(scpi_commands_builtin, partial)) { + return true; + } + + // User commands + return match_any_cmd_from_array(scpi_commands, partial); +} + + +static bool match_any_cmd_from_array(const SCPI_command_t arr[], bool partial) +{ for (uint16_t i = 0; i < 0xFFFF; i++) { - const SCPI_command_t *cmd = &scpi_cmd_lang[i]; + const SCPI_command_t *cmd = &arr[i]; if (cmd->levels[0][0] == 0) break; // end marker - if (cmd_level_count(cmd) > MAX_LEVEL_COUNT) { + if (cmd_level_count(cmd) > SCPI_MAX_LEVEL_COUNT) { // FAIL, too deep. Bad config continue; } - if (try_match_cmd(cmd, partial)) { + if (match_cmd_do(cmd, partial)) { if (partial) { // match found, OK return true; @@ -535,7 +656,7 @@ static bool pars_match_cmd(bool partial) /** Try to match current state to a given command */ -static bool try_match_cmd(const SCPI_command_t *cmd, bool partial) +static bool match_cmd_do(const SCPI_command_t *cmd, bool partial) { const uint8_t level_cnt = cmd_level_count(cmd); if (pst.cur_level_i > level_cnt) return false; // command too short @@ -562,6 +683,18 @@ static bool try_match_cmd(const SCPI_command_t *cmd, bool partial) } +/** Run the matched command's callback with the arguments */ +static void run_command_callback(void) +{ + if (pst.matched_cmd != NULL) { + pst.matched_cmd->callback(pst.args); // run + } +} + + + +// ---------------------- PARSING ARGS -------------------------- + /** Non-whitespace and non-comma char received in arg. */ static void pars_arg_char(char c) { @@ -645,7 +778,7 @@ static void pars_arg_newline(void) } arg_convert_value(); - pars_run_callback(); + run_command_callback(); pars_reset_cmd(); // start a new command } @@ -661,7 +794,7 @@ static void pars_arg_semicolon(void) } arg_convert_value(); - pars_run_callback(); + run_command_callback(); pars_reset_cmd_keeplevel(); // start a new command, keep level } @@ -709,7 +842,7 @@ static void arg_convert_value(void) break; case SCPI_DT_STRING: - if (strlen(pst.charbuf) > MAX_STRING_LEN) { + if (strlen(pst.charbuf) > SCPI_MAX_STRING_LEN) { printf("ERROR string too long.\n");//TODO error pst.state = PARS_DISCARD_LINE; } else { @@ -728,12 +861,13 @@ static void arg_convert_value(void) pst.arg_i++; } + static void pars_blob_preamble_char(uint8_t c) { if (pst.blob_cnt == 0) { if (!INRANGE(c, '1', '9')) { printf("ERROR expected ASCII 1-9 after #\n");//TODO error - pst.state = PARS_DISCARD_LINE; + pst.state = PARS_DISCARD_LINE;// not enough to remove the blob containing \n return; } @@ -759,7 +893,7 @@ static void pars_blob_preamble_char(uint8_t c) sscanf(pst.charbuf, "%d", &pst.blob_len); pst.args[pst.arg_i].BLOB_LEN = pst.blob_len; - pars_run_callback(); + run_command_callback(); // Call handler, enter special blob mode pst.state = PARS_ARG_BLOB_BODY; diff --git a/scpi_parser.h b/scpi_parser.h index e822a4c..f55cd45 100644 --- a/scpi_parser.h +++ b/scpi_parser.h @@ -1,14 +1,13 @@ #pragma once +#include "scpi_errors.h" + #include #include -#define MAX_CMD_LEN 12 -#define MAX_STRING_LEN 12 -#define MAX_LEVEL_COUNT 4 -#define MAX_PARAM_COUNT 4 - -#define ERR_QUEUE_LEN 4 -#define MAX_ERROR_LEN 100 +#define SCPI_MAX_CMD_LEN 12 +#define SCPI_MAX_STRING_LEN 12 +#define SCPI_MAX_LEVEL_COUNT 4 +#define SCPI_MAX_PARAM_COUNT 4 /** Argument data types */ typedef enum { @@ -26,25 +25,27 @@ typedef union { float FLOAT; int32_t INT; bool BOOL; - char STRING[MAX_STRING_LEN+1]; // terminator + char STRING[SCPI_MAX_STRING_LEN + 1]; // terminator uint32_t BLOB_LEN; } SCPI_argval_t; +// ------ CONFIGURATION -------- + /** * SCPI command preset * NOTE: command array is terminated by {0} - zero in levels[0][0] */ typedef struct { // levels MUST BE FIRST! - const char levels[MAX_LEVEL_COUNT][MAX_CMD_LEN]; // up to 4 parts + const char levels[SCPI_MAX_LEVEL_COUNT][SCPI_MAX_CMD_LEN]; // up to 4 parts // called when the command is completed. BLOB arg must be last in the argument list, // and only the first part is collected. void (*callback)(const SCPI_argval_t *args); // Param types - optional (defaults to zeros) - const SCPI_datatype_t params[MAX_PARAM_COUNT]; // parameter types (0 for unused) + const SCPI_datatype_t params[SCPI_MAX_PARAM_COUNT]; // parameter types (0 for unused) // --- OPTIONAL (only for blob) --- @@ -54,8 +55,9 @@ typedef struct { void (*blob_callback)(const uint8_t *bytes); } SCPI_command_t; -// Zero terminated command struct array -extern const SCPI_command_t scpi_cmd_lang[]; + +// Zero terminated command struct array - must be defined. +extern const SCPI_command_t scpi_commands[]; // --------------- functions -------------------- @@ -66,3 +68,19 @@ extern const SCPI_command_t scpi_cmd_lang[]; */ void scpi_handle_byte(const uint8_t b); +/** Add error to the error queue */ +void scpi_add_error(SCPI_error_t errno, const char *extra); + +/** Get number of errors in the error queue */ +uint8_t scpi_error_count(void); + +/** + * Read and remove one entry from the error queue. + * Returns 0,"No error" if the queue is empty. + * + * The entry is copied to the provided buffer, which must be 256 chars long. + */ +void scpi_read_error(char *buf); + +/** Discard the rest of the currently processed blob */ +void scpi_discard_blob(void);